<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>SpringBoot2入门-01 | 无人深空</title><meta name="keywords" content="SpringBoot2"><meta name="author" content="zcm"><meta name="copyright" content="zcm"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="SpringBoot2高层框架：底层为 Spring Framework，用于整合 Spring 框架 微服务开发 ﻿## 01、基础入门-SpringBoot2 课程介绍  Spring Boot 2 核心技术  Spring Boot 2 响应式编程    学习要求 熟悉 Spring 基础 熟悉 Maven 使用   环境要求 Java8 及以上 Maven 3.3 及以上   学习资料 S">
<meta property="og:type" content="article">
<meta property="og:title" content="SpringBoot2入门-01">
<meta property="og:url" content="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-01/index.html">
<meta property="og:site_name" content="无人深空">
<meta property="og:description" content="SpringBoot2高层框架：底层为 Spring Framework，用于整合 Spring 框架 微服务开发 ﻿## 01、基础入门-SpringBoot2 课程介绍  Spring Boot 2 核心技术  Spring Boot 2 响应式编程    学习要求 熟悉 Spring 基础 熟悉 Maven 使用   环境要求 Java8 及以上 Maven 3.3 及以上   学习资料 S">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg">
<meta property="article:published_time" content="2022-09-23T04:08:14.000Z">
<meta property="article:modified_time" content="2022-09-23T04:08:57.391Z">
<meta property="article:author" content="zcm">
<meta property="article:tag" content="SpringBoot2">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-01/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'SpringBoot2入门-01',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-09-23 12:08:57'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/categories.css?v1"><link rel="stylesheet" href="/css/navigation.css?v1"><link rel="stylesheet" href="/css/custom.css?v1"><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://s2.loli.net/2022/09/22/36LN4kcjYOd9HT8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">11</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">无人深空</a></span><div id="menus"></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div><div id="nav-right"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i></a></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">SpringBoot2入门-01</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-09-23T04:08:14.000Z" title="发表于 2022-09-23 12:08:14">2022-09-23</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-09-23T04:08:57.391Z" title="更新于 2022-09-23 12:08:57">2022-09-23</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Spring/">Spring</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">19.8k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>97分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="SpringBoot2入门-01"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><h1 id="SpringBoot2"><a href="#SpringBoot2" class="headerlink" title="SpringBoot2"></a><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV19K4y1L7MT?p=1">SpringBoot2</a></h1><p>高层框架：底层为 Spring Framework，用于整合 Spring 框架</p>
<p>微服务开发</p>
<p>﻿## 01、基础入门-SpringBoot2 课程介绍</p>
<ol>
<li><p>Spring Boot 2 核心技术</p>
</li>
<li><p>Spring Boot 2 响应式编程</p>
</li>
</ol>
<ul>
<li>学习要求<ul>
<li>熟悉 Spring 基础</li>
<li>熟悉 Maven 使用</li>
</ul>
</li>
<li>环境要求<ul>
<li>Java8 及以上</li>
<li>Maven 3.3 及以上</li>
</ul>
</li>
<li>学习资料<ul>
<li><a target="_blank" rel="noopener" href="https://spring.io/">Spring官网</a></li>
<li><a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">Spring Boot官网</a></li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/">Spring Boot官方文档</a></li>
<li><a target="_blank" rel="noopener" href="https://www.yuque.com/atguigu/springboot">本课程文档地址</a></li>
<li><a href="https://gitee.com/leifengyang/springboot2">源码地址</a></li>
</ul>
</li>
</ul>
<h2 id="02、基础入门-Spring-生态圈"><a href="#02、基础入门-Spring-生态圈" class="headerlink" title="02、基础入门-Spring 生态圈"></a>02、基础入门-Spring 生态圈</h2><h3 id="Spring-能做什么"><a href="#Spring-能做什么" class="headerlink" title="Spring 能做什么"></a>Spring 能做什么</h3><h4 id="Spring-的能力"><a href="#Spring-的能力" class="headerlink" title="Spring 的能力"></a>Spring 的能力</h4><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004146543.png" alt="在这里插入图片描述"></p>
<h4 id="Spring-的生态"><a href="#Spring-的生态" class="headerlink" title="Spring 的生态"></a>Spring 的生态</h4><p>覆盖了：</p>
<ul>
<li>web开发</li>
<li>数据访问</li>
<li>安全控制</li>
<li>分布式</li>
<li>消息服务</li>
<li>移动开发</li>
<li>批处理</li>
<li>……</li>
</ul>
<h4 id="Spring5-重大升级"><a href="#Spring5-重大升级" class="headerlink" title="Spring5 重大升级"></a>Spring5 重大升级</h4><ul>
<li>响应式编程</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004250581.png" alt="在这里插入图片描述"></p>
<ul>
<li>内部源码设计</li>
</ul>
<p>基于 Java8 的一些新特性，如：接口默认实现、重新设计源码架构</p>
<h3 id="为什么用-SpringBoot"><a href="#为什么用-SpringBoot" class="headerlink" title="为什么用 SpringBoot"></a>为什么用 SpringBoot</h3><blockquote>
<p>Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.<a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">link</a></p>
<p>能快速创建出生产级别的 Spring 应用</p>
</blockquote>
<h4 id="SpringBoot-优点"><a href="#SpringBoot-优点" class="headerlink" title="SpringBoot 优点"></a>SpringBoot 优点</h4><ul>
<li><p>Create stand-alone Spring applications</p>
<ul>
<li>创建独立 Spring 应用</li>
</ul>
</li>
<li><p>Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)</p>
<ul>
<li>内嵌 web 服务器</li>
</ul>
</li>
<li><p>Provide opinionated ‘starter’ dependencies to simplify your build configuration</p>
<ul>
<li>自动 starter 依赖，简化构建配置</li>
</ul>
</li>
<li><p>Automatically configure Spring and 3rd party libraries whenever possible</p>
<ul>
<li>自动配置 Spring 以及第三方功能</li>
</ul>
</li>
<li><p>Provide production-ready features such as metrics, health checks, and externalized configuration</p>
<ul>
<li>提供生产级别的监控、健康检查及外部化配置</li>
</ul>
</li>
<li><p>Absolutely no code generation and no requirement for XML configuration</p>
<ul>
<li>无代码生成、无需编写 XML</li>
</ul>
</li>
<li><p>&#x3D;&#x3D;<strong>SpringBoot 是整合 Spring 技术栈的一站式框架</strong>&#x3D;&#x3D;</p>
</li>
<li><p>&#x3D;&#x3D;<strong>SpringBoot 是简化 Spring 技术栈的快速开发脚手架</strong>&#x3D;&#x3D;</p>
</li>
</ul>
<h4 id="SpringBoot-缺点"><a href="#SpringBoot-缺点" class="headerlink" title="SpringBoot 缺点"></a>SpringBoot 缺点</h4><ul>
<li>人称版本帝，迭代快，需要时刻关注变化</li>
<li>封装太深，内部原理复杂，不容易精通</li>
</ul>
<h2 id="03、基础入门-SpringBoot的大时代背景"><a href="#03、基础入门-SpringBoot的大时代背景" class="headerlink" title="03、基础入门-SpringBoot的大时代背景"></a>03、基础入门-SpringBoot的大时代背景</h2><h3 id="微服务"><a href="#微服务" class="headerlink" title="微服务"></a>微服务</h3><blockquote>
<p>In short, the <strong>microservice architectural style</strong> is an approach to developing a single application as a <strong>suite of small services</strong>, each <strong>running in its own process</strong> and communicating with <strong>lightweight</strong> mechanisms, often an <strong>HTTP</strong> resource API. These services are built around <strong>business capabilities</strong> and <strong>independently deployable</strong> by fully <strong>automated deployment</strong> machinery. There is a bare minimum of centralized management of these services, which may be <strong>written in different programming languages</strong> and use different data storage technologies.——<a target="_blank" rel="noopener" href="https://martinfowler.com/articles/microservices.html">James Lewis and Martin Fowler (2014)</a></p>
</blockquote>
<ul>
<li>微服务是一种架构风格</li>
<li>一个应用拆分为一组小型服务</li>
<li>每个服务运行在自己的进程内，也就是可独立部署和升级</li>
<li>服务之间使用轻量级HTTP交互</li>
<li>服务围绕业务功能拆分</li>
<li>可以由全自动部署机制独立部署</li>
<li>去中心化，服务自治。服务可以使用不同的语言、不同的存储技术</li>
</ul>
<h3 id="分布式"><a href="#分布式" class="headerlink" title="分布式"></a>分布式</h3><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/2021020500434620.png" alt="在这里插入图片描述"></p>
<h4 id="分布式的困难"><a href="#分布式的困难" class="headerlink" title="分布式的困难"></a>分布式的困难</h4><ul>
<li>远程调用</li>
<li>服务发现</li>
<li>负载均衡</li>
<li>服务容错</li>
<li>配置管理</li>
<li>服务监控</li>
<li>链路追踪</li>
<li>日志管理</li>
<li>任务调度</li>
<li>……</li>
</ul>
<h4 id="分布式的解决"><a href="#分布式的解决" class="headerlink" title="分布式的解决"></a>分布式的解决</h4><ul>
<li>SpringBoot + SpringCloud</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004523307.png" alt="在这里插入图片描述"></p>
<h3 id="云原生"><a href="#云原生" class="headerlink" title="云原生"></a>云原生</h3><p>原生应用如何上云：Cloud Native</p>
<h4 id="上云的困难"><a href="#上云的困难" class="headerlink" title="上云的困难"></a>上云的困难</h4><ul>
<li>服务自愈</li>
<li>弹性伸缩</li>
<li>服务隔离</li>
<li>自动化部署</li>
<li>灰度发布</li>
<li>流量治理</li>
<li>……</li>
</ul>
<h4 id="上云的解决"><a href="#上云的解决" class="headerlink" title="上云的解决"></a>上云的解决</h4><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004621290.png" alt="在这里插入图片描述"></p>
<h2 id="04、基础入门-SpringBoot官方文档架构"><a href="#04、基础入门-SpringBoot官方文档架构" class="headerlink" title="04、基础入门-SpringBoot官方文档架构"></a>04、基础入门-SpringBoot官方文档架构</h2><ul>
<li><a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">Spring Boot官网</a></li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/">Spring Boot官方文档</a></li>
</ul>
<h3 id="官网文档架构"><a href="#官网文档架构" class="headerlink" title="官网文档架构"></a>官网文档架构</h3><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004733270.png" alt="在这里插入图片描述"><br><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205004828702.png" alt="在这里插入图片描述"></p>
<p><a target="_blank" rel="noopener" href="https://github.com/spring-projects/spring-boot/wiki#release-notes">查看版本新特性</a></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005342147.png" alt="在这里插入图片描述"></p>
<h2 id="05、基础入门-SpringBoot-HelloWorld"><a href="#05、基础入门-SpringBoot-HelloWorld" class="headerlink" title="05、基础入门 - SpringBoot-HelloWorld"></a>05、基础入门 - SpringBoot-HelloWorld</h2><h3 id="系统要求"><a href="#系统要求" class="headerlink" title="系统要求"></a>系统要求</h3><ul>
<li>Java 8</li>
<li>Maven 3.3+</li>
<li>IntelliJ IDEA 2019.1.2</li>
</ul>
<h4 id="Maven配置文件"><a href="#Maven配置文件" class="headerlink" title="Maven配置文件"></a>Maven配置文件</h4><p>新添内容：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirrors</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">id</span>&gt;</span>nexus-aliyun<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">name</span>&gt;</span>Nexus aliyun<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mirrors</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">profiles</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">id</span>&gt;</span>jdk-1.8<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line"></span><br><span class="line">		<span class="tag">&lt;<span class="name">activation</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">activeByDefault</span>&gt;</span>true<span class="tag">&lt;/<span class="name">activeByDefault</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">jdk</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">jdk</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;/<span class="name">activation</span>&gt;</span></span><br><span class="line"></span><br><span class="line">		<span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">maven.compiler.compilerVersion</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.compilerVersion</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">profiles</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="HelloWorld-项目"><a href="#HelloWorld-项目" class="headerlink" title="HelloWorld 项目"></a>HelloWorld 项目</h3><p>需求：浏览发送 <code>/hello</code> 请求，响应 “Hello，Spring Boot 2”</p>
<h4 id="创建-maven-工程"><a href="#创建-maven-工程" class="headerlink" title="创建 maven 工程"></a>创建 maven 工程</h4><h4 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="创建主程序"><a href="#创建主程序" class="headerlink" title="创建主程序"></a>创建主程序</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(MainApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="编写业务"><a href="#编写业务" class="headerlink" title="编写业务"></a>编写业务</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">handle01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello, Spring Boot 2!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="运行-amp-测试"><a href="#运行-amp-测试" class="headerlink" title="运行&amp;测试"></a>运行&amp;测试</h4><ul>
<li>运行 <code>MainApplication</code> 类</li>
<li>浏览器输入 <code>http://localhost:8080/hello</code>，将会输出 <code>Hello, Spring Boot 2!</code>。</li>
</ul>
<h4 id="设置配置"><a href="#设置配置" class="headerlink" title="设置配置"></a>设置配置</h4><p>maven 工程的 resource 文件夹中创建 application.properties 文件</p>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 设置端口号</span></span><br><span class="line"><span class="attr">server.port</span>=<span class="string">8888</span></span><br><span class="line"><span class="comment"># 访问改为: http://localhost:8888/hello</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/reference/html/appendix-application-properties.html#common-application-properties-server">更多配置信息</a></p>
<h4 id="打包部署"><a href="#打包部署" class="headerlink" title="打包部署"></a>打包部署</h4><p>在 <code>pom.xml</code> 添加</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">			<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>在 IDEA 的 Maven 插件上点击运行 clean 、package，把 helloworld 工程项目的打包成 jar 包</p>
<p>打包好的 jar 包被生成在 helloworld 工程项目的 target 文件夹内</p>
<p>用 cmd 运行 <code>java -jar boot-01-helloworld-1.0-SNAPSHOT.jar</code>，既可以运行 helloworld 工程项目</p>
<p>将 jar 包直接在目标服务器执行即可</p>
<h2 id="06、基础入门-SpringBoot-依赖管理特性"><a href="#06、基础入门-SpringBoot-依赖管理特性" class="headerlink" title="06、基础入门-SpringBoot-依赖管理特性"></a>06、基础入门-SpringBoot-依赖管理特性</h2><ul>
<li>父项目做依赖管理</li>
</ul>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line">依赖管理</span><br><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">上面项目的父项目如下：</span><br><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">它几乎声明了所有开发中常用的依赖的版本号，自动版本仲裁机制</span><br></pre></td></tr></table></figure>

<ul>
<li>开发导入 starter 场景启动器<ol>
<li>见到很多 <code>spring-boot-starter-*</code> ： <code>*</code> 代表某种场景。如 <code>spring-boot-starter-web</code></li>
<li>只要引入 starter，这个场景的所有常规需要的依赖我们都自动引入</li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter">更多 SpringBoot 所有支持的场景</a></li>
<li>见到的  <code>*-spring-boot-starter</code>： 第三方为我们提供的简化开发的场景启动器</li>
</ol>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line">所有场景启动器最底层的依赖</span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>无需关注版本号，自动版本仲裁</p>
<ol>
<li>引入依赖默认都可以不写版本</li>
<li>引入非版本仲裁的 jar，要写版本号</li>
</ol>
</li>
<li><p>可以修改默认版本号</p>
<ol>
<li>查看 <code>spring-boot-dependencies</code> 里面规定当前依赖的版本用的 key</li>
<li>在当前项目里面重写配置，如下面的代码</li>
</ol>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">mysql.version</span>&gt;</span>5.1.43<span class="tag">&lt;/<span class="name">mysql.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br></pre></td></tr></table></figure>

<hr>
<p>IDEA快捷键：</p>
<ul>
<li><code> ctrl + shift + alt + U</code>：以图的方式显示项目中依赖之间的关系。</li>
<li><code>alt + ins</code>：相当于Eclipse的 Ctrl + N，创建新类，新包等。</li>
</ul>
<h2 id="07、基础入门-SpringBoot-自动配置特性"><a href="#07、基础入门-SpringBoot-自动配置特性" class="headerlink" title="07、基础入门-SpringBoot-自动配置特性"></a>07、基础入门-SpringBoot-自动配置特性</h2><ul>
<li>自动配好 Tomcat<ul>
<li>引入 Tomcat 依赖（见上<code>依赖管理</code>）</li>
<li>配置 Tomcat</li>
</ul>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-tomcat<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>自动配好 SpringMVC</p>
<ul>
<li>引入 SpringMVC 全套组件</li>
<li>自动配好 SpringMVC 常用组件（功能）</li>
</ul>
</li>
<li><p>自动配好 Web 常见功能，如：字符编码问题</p>
<ul>
<li>SpringBoot 帮我们配置好了所有 web 开发的常见场景：如 <code>DispatcherServlet</code>、<code>CharacterEncodingFilter</code>、各种 <code>Resolver</code> 等</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">    String[] names = run.getBeanDefinitionNames();</span><br><span class="line">    <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>默认的包结构<ul>
<li><strong>主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来</strong></li>
<li><strong>无需以前的包扫描配置</strong></li>
<li>想要改变扫描路径<ul>
<li><code>@SpringBootApplication(scanBasePackages=&quot;com.atguigu&quot;)</code></li>
<li>或者 <code>@ComponentScan</code> 指定扫描路径</li>
</ul>
</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span> </span><br><span class="line">等同于</span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.lun&quot;)</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>各种配置拥有默认值</p>
<ul>
<li>默认配置最终都是映射到某个类上，如：<code>MultipartProperties</code></li>
<li>配置文件的值最终会绑定每个类上，这个类会在容器中创建对象</li>
</ul>
</li>
<li><p>按需加载所有自动配置项</p>
<ul>
<li>非常多的 starter</li>
<li>引入了哪些场景这个场景的自动配置才会开启</li>
<li>SpringBoot 所有的自动配置功能都在 <code>spring-boot-autoconfigure</code> 包里面</li>
<li></li>
</ul>
</li>
<li><p>……</p>
</li>
</ul>
<h2 id="08、底层注解-Configuration详解"><a href="#08、底层注解-Configuration详解" class="headerlink" title="08、底层注解-@Configuration详解"></a>08、底层注解-@Configuration详解</h2><ul>
<li>基本使用<ul>
<li>Full 模式与 Lite 模式</li>
<li>示例</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 1、配置类里面使用<span class="doctag">@Bean</span>标注在方法上给容器注册组件，默认也是单实例的</span></span><br><span class="line"><span class="comment"> * 2、配置类本身也是组件</span></span><br><span class="line"><span class="comment"> * 3、proxyBeanMethods：代理bean的方法</span></span><br><span class="line"><span class="comment"> *      Full(proxyBeanMethods = true)（保证每个<span class="doctag">@Bean</span>方法被调用多少次返回的组件都是单实例的）（默认）</span></span><br><span class="line"><span class="comment"> *      Lite(proxyBeanMethods = false)（每个<span class="doctag">@Bean</span>方法被调用多少次返回的组件都是新创建的）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span> <span class="comment">//告诉SpringBoot这是一个配置类 == 配置文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span> <span class="comment">//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值，就是组件在容器中的实例</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">user01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">zhangsan</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;zhangsan&quot;</span>, <span class="number">18</span>);</span><br><span class="line">        <span class="comment">//user组件依赖了Pet组件</span></span><br><span class="line">        zhangsan.setPet(tomcatPet());</span><br><span class="line">        <span class="keyword">return</span> zhangsan;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以 tom 作为组件 id，而不用 tomcatPet</span></span><br><span class="line">    <span class="meta">@Bean(&quot;tom&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Pet <span class="title function_">tomcatPet</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Pet</span>(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<p>@Configuration测试代码如下:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.atguigu.boot&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    	<span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    	<span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">        String[] names = run.getBeanDefinitionNames();</span><br><span class="line">        <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    	<span class="comment">//3、从容器中获取组件</span></span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom01</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom02</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;组件：&quot;</span>+(tom01 == tom02));</span><br><span class="line"></span><br><span class="line">    	<span class="comment">//4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892</span></span><br><span class="line">        <span class="type">MyConfig</span> <span class="variable">bean</span> <span class="operator">=</span> run.getBean(MyConfig.class);</span><br><span class="line">        System.out.println(bean);</span><br><span class="line"></span><br><span class="line">    	<span class="comment">//如果@Configuration(proxyBeanMethods = true(默认为 true，可以不写))代理对象调用方法。SpringBoot 总会检查这个组件是否在容器中有</span></span><br><span class="line">        <span class="comment">//保持组件单实例</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> bean.user01();</span><br><span class="line">        <span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> bean.user01();</span><br><span class="line">        System.out.println(user == user1);</span><br><span class="line"></span><br><span class="line">        <span class="type">User</span> <span class="variable">user01</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;user01&quot;</span>, User.class);</span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;用户的宠物：&quot;</span>+(user01.getPet() == tom));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>最佳实战<ul>
<li>配置类组件之间<strong>无依赖关系</strong>用 Lite 模式加速容器启动过程，减少判断</li>
<li>配置类组件之间<strong>有依赖关系</strong>（如 User 中有 Pet 成员变量），方法会被调用得到之前单实例组件，用 Full 模式（默认）</li>
</ul>
</li>
</ul>
<blockquote>
<p>lite 英 [laɪt]   美 [laɪt]<br>adj. 低热量的，清淡的(light的一种拼写方法);类似…的劣质品</p>
</blockquote>
<hr>
<p>IDEA 快捷键：</p>
<ul>
<li><code>Alt + Ins</code>: 生成getter，setter、构造器等代码</li>
<li><code>Ctrl + Alt + B</code>: 查看类的具体实现代码</li>
</ul>
<h2 id="09、底层注解-Import-导入组件"><a href="#09、底层注解-Import-导入组件" class="headerlink" title="09、底层注解-@Import 导入组件"></a>09、底层注解-@Import 导入组件</h2><p>@Bean、@Component、@Controller、@Service、@Repository，它们是Spring的基本标签，在Spring Boot中并未改变它们原来的功能。</p>
<p>@ComponentScan 在<a href="#">07、基础入门-SpringBoot-自动配置特性</a>有用例。</p>
<p>@Import({User.class, DBHelper.class})给容器中<strong>自动创建出这两个类型的组件</strong>、默认组件的名字就是<strong>全类名</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Import(&#123;User.class, DBHelper.class&#125;)</span></span><br><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span> <span class="comment">//告诉SpringBoot这是一个配置类 == 配置文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试类：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//1、返回我们IOC容器</span></span><br><span class="line"><span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//5、获取组件</span></span><br><span class="line">String[] beanNamesForType = run.getBeanNamesForType(User.class);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (String s : beanNamesForType) &#123;</span><br><span class="line">    System.out.println(s);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">DBHelper</span> <span class="variable">bean1</span> <span class="operator">=</span> run.getBean(DBHelper.class);</span><br><span class="line">System.out.println(bean1);</span><br></pre></td></tr></table></figure>

<h2 id="10、底层注解-Conditional-条件装配"><a href="#10、底层注解-Conditional-条件装配" class="headerlink" title="10、底层注解-@Conditional 条件装配"></a>10、底层注解-@Conditional 条件装配</h2><p><strong>条件装配：满足 Conditional 指定的条件，则进行组件注入</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005453173.png" alt="在这里插入图片描述"></p>
<p>用 @ConditionalOnMissingBean 举例说明</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(name = &quot;tom&quot;)</span><span class="comment">//没有tom名字的Bean时，MyConfig类的Bean才能生效。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">user01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">zhangsan</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;zhangsan&quot;</span>, <span class="number">18</span>);</span><br><span class="line">        zhangsan.setPet(tomcatPet());</span><br><span class="line">        <span class="keyword">return</span> zhangsan;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean(&quot;tom22&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Pet <span class="title function_">tomcatPet</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Pet</span>(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">    String[] names = run.getBeanDefinitionNames();</span><br><span class="line">    <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">tom</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;tom&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中Tom组件：&quot;</span>+tom);<span class="comment">//false</span></span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">user01</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;user01&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中user01组件：&quot;</span>+user01);<span class="comment">//true</span></span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">tom22</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;tom22&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中tom22组件：&quot;</span>+tom22);<span class="comment">//true</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="11、底层注解-ImportResource-导入-Spring-配置文件"><a href="#11、底层注解-ImportResource-导入-Spring-配置文件" class="headerlink" title="11、底层注解-@ImportResource 导入 Spring 配置文件"></a>11、底层注解-@ImportResource 导入 Spring 配置文件</h2><p>如公司使用 bean.xml 文件生成配置 bean，然而为了省事继续复用 bean.xml，@ImportResource 粉墨登场</p>
<p>bean.xml：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">...</span>&quot;&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;haha&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.lun.boot.bean.User&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;zhangsan&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;age&quot;</span> <span class="attr">value</span>=<span class="string">&quot;18&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;hehe&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.lun.boot.bean.Pet&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;tomcat&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>使用方法：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ImportResource(&quot;classpath:beans.xml&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试类：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">	<span class="type">boolean</span> <span class="variable">haha</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;haha&quot;</span>);</span><br><span class="line">	<span class="type">boolean</span> <span class="variable">hehe</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;hehe&quot;</span>);</span><br><span class="line">	System.out.println(<span class="string">&quot;haha：&quot;</span>+haha);<span class="comment">//true</span></span><br><span class="line">	System.out.println(<span class="string">&quot;hehe：&quot;</span>+hehe);<span class="comment">//true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="12、底层注解-ConfigurationProperties-配置绑定"><a href="#12、底层注解-ConfigurationProperties-配置绑定" class="headerlink" title="12、底层注解-@ConfigurationProperties 配置绑定"></a>12、底层注解-@ConfigurationProperties 配置绑定</h2><p>如何使用 Java 读取到 properties 文件中的内容，并且把它封装到 JavaBean 中，以供随时使用</p>
<p>传统方法：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">getProperties</span> &#123;</span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> FileNotFoundException, IOException &#123;</span><br><span class="line">         <span class="type">Properties</span> <span class="variable">pps</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">         pps.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;a.properties&quot;</span>));</span><br><span class="line">         <span class="type">Enumeration</span> <span class="variable">enum1</span> <span class="operator">=</span> pps.propertyNames();<span class="comment">//得到配置文件的名字</span></span><br><span class="line">         <span class="keyword">while</span>(enum1.hasMoreElements()) &#123;</span><br><span class="line">             <span class="type">String</span> <span class="variable">strKey</span> <span class="operator">=</span> (String) enum1.nextElement();</span><br><span class="line">             <span class="type">String</span> <span class="variable">strValue</span> <span class="operator">=</span> pps.getProperty(strKey);</span><br><span class="line">             System.out.println(strKey + <span class="string">&quot;=&quot;</span> + strValue);</span><br><span class="line">             <span class="comment">//封装到JavaBean。</span></span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>Spring Boot一种配置配置绑定：</p>
<p>@ConfigurationProperties + @Component</p>
<p>假设有配置文件 application.properties</p>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="attr">mycar.brand</span>=<span class="string">BYD</span></span><br><span class="line"><span class="attr">mycar.price</span>=<span class="string">100000</span></span><br></pre></td></tr></table></figure>

<p><strong>只有在容器中的组件，才会拥有 SpringBoot 提供的强大功能</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;mycar&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<p><strong>Spring Boot 另一种配置配置绑定：</strong></p>
<p>@EnableConfigurationProperties + @ConfigurationProperties</p>
<ol>
<li>开启 Car 配置绑定功能</li>
<li>把这个 Car 这个组件自动注册到容器中</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@EnableConfigurationProperties(Car.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;mycar&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="13、自动配置【源码分析】-自动包规则原理"><a href="#13、自动配置【源码分析】-自动包规则原理" class="headerlink" title="13、自动配置【源码分析】- 自动包规则原理"></a>13、自动配置【源码分析】- 自动包规则原理</h2><p>Spring Boot 应用的启动类：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(MainApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>分析下 <code>@SpringBootApplication</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    excludeFilters = &#123;@Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;TypeExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">), @Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;AutoConfigurationExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">)&#125;</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootApplication &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>重点分析 <code>@SpringBootConfiguration</code>，<code>@ComponentScan</code>，<code>@EnableAutoConfiguration</code></p>
<h3 id="SpringBootConfiguration"><a href="#SpringBootConfiguration" class="headerlink" title="@SpringBootConfiguration"></a>@SpringBootConfiguration</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootConfiguration &#123;</span><br><span class="line">    <span class="meta">@AliasFor(</span></span><br><span class="line"><span class="meta">        annotation = Configuration.class</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">proxyBeanMethods</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>@Configuration</code>代表当前是一个配置类</p>
<h3 id="ComponentScan"><a href="#ComponentScan" class="headerlink" title="@ComponentScan"></a>@ComponentScan</h3><p>指定扫描哪些 Spring 注解</p>
<p>@ComponentScan 在<a href="#">07、基础入门-SpringBoot-自动配置特性</a>有用例</p>
<h3 id="EnableAutoConfiguration"><a href="#EnableAutoConfiguration" class="headerlink" title="@EnableAutoConfiguration"></a>@EnableAutoConfiguration</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@AutoConfigurationPackage</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationImportSelector.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAutoConfiguration &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">ENABLED_OVERRIDE_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.boot.enableautoconfiguration&quot;</span>;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] exclude() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String[] excludeName() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>重点分析 <code>@AutoConfigurationPackage</code>，<code>@Import(AutoConfigurationImportSelector.class)</code></p>
<h4 id="AutoConfigurationPackage"><a href="#AutoConfigurationPackage" class="headerlink" title="@AutoConfigurationPackage"></a>@AutoConfigurationPackage</h4><p>标签名直译为：自动配置包，指定了默认的包规则</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationPackages.Registrar.class)</span><span class="comment">//给容器中导入一个组件</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoConfigurationPackage &#123;</span><br><span class="line">    String[] basePackages() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">    Class&lt;?&gt;[] basePackageClasses() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol>
<li>利用 Registrar 给容器中导入一系列组件</li>
<li>将指定的一个包下的所有组件导入进 MainApplication 所在包下</li>
</ol>
<h2 id="14、自动配置【源码分析】-初始加载自动配置类"><a href="#14、自动配置【源码分析】-初始加载自动配置类" class="headerlink" title="14、自动配置【源码分析】-初始加载自动配置类"></a>14、自动配置【源码分析】-初始加载自动配置类</h2><h4 id="Import-AutoConfigurationImportSelector-class"><a href="#Import-AutoConfigurationImportSelector-class" class="headerlink" title="@Import(AutoConfigurationImportSelector.class)"></a>@Import(AutoConfigurationImportSelector.class)</h4><ol>
<li>利用 <code>getAutoConfigurationEntry(annotationMetadata);</code> 给容器中批量导入一些组件</li>
<li>调用 <code>List&lt;String&gt; configurations = getCandidateConfigurations(annotationMetadata, attributes)</code> 获取到所有需要导入到容器中的配置类</li>
<li>利用工厂加载 <code>Map&lt;String, List&lt;String&gt;&gt; loadSpringFactories(@Nullable ClassLoader classLoader);</code> 得到所有的组件</li>
<li>从 <code>META-INF/spring.factories</code> 位置来加载一个文件<ul>
<li>默认扫描我们当前系统里面所有 <code>META-INF/spring.factories</code> 位置的文件</li>
<li><code>spring-boot-autoconfigure-2.3.4.RELEASE.jar</code> 包里面也有 <code>META-INF/spring.factories</code></li>
</ul>
</li>
</ol>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005536620.png" alt="在这里插入图片描述"></p>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类</span></span><br><span class="line"><span class="comment"># spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories</span></span><br><span class="line"><span class="comment"># Auto Configure</span></span><br><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\</span></span><br><span class="line"><span class="string">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\</span></span><br><span class="line"><span class="string">...</span></span><br></pre></td></tr></table></figure>

<p>虽然我们 127 个场景的所有自动配置启动的时候默认全部加载，但是 <code>xxxxAutoConfiguration</code> 按照条件装配规则（<code>@Conditional</code>），最终会按需配置</p>
<p>如 <code>AopAutoConfiguration</code> 类：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">    prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">    name = &quot;auto&quot;,</span></span><br><span class="line"><span class="meta">    havingValue = &quot;true&quot;,</span></span><br><span class="line"><span class="meta">    matchIfMissing = true</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AopAutoConfiguration</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AopAutoConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">	...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="15、自动配置【源码分析】-自动配置流程"><a href="#15、自动配置【源码分析】-自动配置流程" class="headerlink" title="15、自动配置【源码分析】-自动配置流程"></a>15、自动配置【源码分析】-自动配置流程</h2><p>以<code>DispatcherServletAutoConfiguration</code>的内部类<code>DispatcherServletConfiguration</code>为例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@ConditionalOnBean(MultipartResolver.class)</span>  <span class="comment">//容器中有这个类型组件</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)</span> <span class="comment">//容器中没有这个名字 multipartResolver 的组件</span></span><br><span class="line"><span class="keyword">public</span> MultipartResolver <span class="title function_">multipartResolver</span><span class="params">(MultipartResolver resolver)</span> &#123;</span><br><span class="line">	<span class="comment">//给@Bean标注的方法传入了对象参数，这个参数的值就会从容器中找。</span></span><br><span class="line">	<span class="comment">//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范</span></span><br><span class="line">	<span class="comment">// Detect if the user has created a MultipartResolver but named it incorrectly</span></span><br><span class="line">	<span class="keyword">return</span> resolver;<span class="comment">//给容器中加入了文件上传解析器；</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>SpringBoot默认会在底层配好所有的组件，但是<strong>如果用户自己配置了以用户的优先</strong></p>
<p><strong>总结</strong>：</p>
<ul>
<li>SpringBoot先加载所有的自动配置类  xxxxxAutoConfiguration</li>
<li>每个自动配置类按照条件进行生效，默认都会绑定配置文件指定的值。（xxxxProperties 里面读取，xxxProperties 和配置文件进行了绑定）</li>
<li>生效的配置类就会给容器中装配很多组件</li>
<li>只要容器中有这些组件，相当于这些功能就有了</li>
<li>定制化配置<ul>
<li>用户直接自己 @Bean 替换底层的组件</li>
<li>用户去看这个组件是获取的配置文件什么值就去修改</li>
</ul>
</li>
</ul>
<p><strong>xxxxxAutoConfiguration —&gt; 组件 —&gt; xxxxProperties里面拿值  —-&gt; application.properties</strong></p>
<h2 id="16、最佳实践-SpringBoot应用如何编写"><a href="#16、最佳实践-SpringBoot应用如何编写" class="headerlink" title="16、最佳实践-SpringBoot应用如何编写"></a>16、最佳实践-SpringBoot应用如何编写</h2><ul>
<li>引入场景依赖<ul>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter">官方文档</a></li>
</ul>
</li>
<li>查看自动配置了哪些（选做）<ul>
<li>自己分析，引入场景对应的自动配置一般都生效了</li>
<li>配置文件中 debug&#x3D;true 开启自动配置报告<ul>
<li>Negative（不生效）</li>
<li>Positive（生效）</li>
</ul>
</li>
</ul>
</li>
<li>是否需要修改<ul>
<li>参照文档修改配置项<ul>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties">官方文档</a></li>
<li>自己分析。xxxxProperties 绑定了配置文件的哪些</li>
</ul>
</li>
<li>自定义加入或者替换组件<ul>
<li>@Bean、@Component…</li>
</ul>
</li>
<li>自定义器  XXXXXCustomizer</li>
<li>……</li>
</ul>
</li>
</ul>
<h2 id="17、最佳实践-Lombok-简化开发"><a href="#17、最佳实践-Lombok-简化开发" class="headerlink" title="17、最佳实践-Lombok 简化开发"></a>17、最佳实践-Lombok 简化开发</h2><p>Lombok 用标签方式代替构造器、getter&#x2F;setter、toString() 等鸡肋代码</p>
<p>spring boot 已经管 理Lombok。引入依赖：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>IDEA 中 File-&gt;Settings-&gt;Plugins，搜索安装 Lombok 插件</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="comment">//@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="meta">@EqualsAndHashCode</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">(String name,Integer age)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>简化日志开发</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">handle01</span><span class="params">(<span class="meta">@RequestParam(&quot;name&quot;)</span> String name)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;请求进来了....&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello, Spring Boot 2!&quot;</span>+<span class="string">&quot;你好：&quot;</span>+name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="18、最佳实践-dev-tools"><a href="#18、最佳实践-dev-tools" class="headerlink" title="18、最佳实践-dev-tools"></a>18、最佳实践-dev-tools</h2><blockquote>
<p>Spring Boot includes an additional set of tools that can make the application development experience a little more pleasant. The <code>spring-boot-devtools</code> module can be included in any project to provide additional development-time features.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools">link</a></p>
<p>Applications that use <code>spring-boot-devtools</code> automatically restart whenever files on the classpath change. This can be a useful feature when working in an IDE, as it gives a very fast feedback loop for code changes. By default, any entry on the classpath that points to a directory is monitored for changes. Note that certain resources, such as static assets and view templates, <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools-restart-exclude">do not need to restart the application</a>.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools-restart">link</a></p>
<p>Triggering a restart</p>
<p>As DevTools monitors classpath resources, the only way to trigger a restart is to update the classpath. The way in which you cause the classpath to be updated depends on the IDE that you are using:</p>
<ul>
<li>In Eclipse, saving a modified file causes the classpath to be updated and triggers a restart.</li>
<li>In IntelliJ IDEA, building the project (<code>Build -&gt; Build Project</code>)(shortcut: Ctrl+F9) has the same effect.</li>
</ul>
</blockquote>
<p>添加依赖：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-devtools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>在IDEA中，项目或者页面修改以后：Ctrl+F9。</p>
<h2 id="19、最佳实践-Spring-Initailizr（项目开发向导）"><a href="#19、最佳实践-Spring-Initailizr（项目开发向导）" class="headerlink" title="19、最佳实践-Spring Initailizr（项目开发向导）"></a>19、最佳实践-Spring Initailizr（项目开发向导）</h2><p><a target="_blank" rel="noopener" href="https://start.spring.io/">Spring Initailizr </a>是创建 Spring Boot 工程向导。</p>
<p>在IDEA中，菜单栏New -&gt; Project -&gt; Spring Initailizr</p>
<h2 id="20、配置文件-yaml的用法"><a href="#20、配置文件-yaml的用法" class="headerlink" title="20、配置文件-yaml的用法"></a>20、配置文件-yaml的用法</h2><p>同以前的 properties 用法</p>
<p>YAML 是 “YAML Ain’t Markup Language”（YAML 不是一种标记语言）的递归缩写。在开发的这种语言时，YAML 的意思其实是：”Yet Another Markup Language”（仍是一种标记语言）</p>
<p><strong>非常适合用来做以数据为中心的配置文件</strong></p>
<h3 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h3><ul>
<li>key: value；<strong>kv之间有空格</strong></li>
<li>大小写敏感</li>
<li>使用缩进表示层级关系</li>
<li><strong>缩进不允许使用 tab，只允许空格</strong></li>
<li><strong>缩进的空格数不重要，只要相同层级的元素左对齐即可</strong></li>
<li><code>#</code> 表示注释</li>
<li>字符串无需加引号，如果要加，单引号<code>&#39;&#39;</code>、双引号 <code>&quot;&quot;</code> 表示字符串内容会 <code>被转义</code>、<code>不转义</code></li>
</ul>
<h3 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h3><ul>
<li>字面量：单个的、不可再分的值。date、boolean、string、number、null</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">k:</span> <span class="string">v</span></span><br></pre></td></tr></table></figure>

<ul>
<li>对象：键值对的集合。map、hash、set、object</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">#行内写法：  </span></span><br><span class="line"><span class="attr">k:</span> &#123;<span class="string">k1:v1</span>,<span class="string">k2:v2</span>,<span class="string">k3:v3</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">#或</span></span><br><span class="line"><span class="attr">k:</span> </span><br><span class="line">  <span class="attr">k1:</span> <span class="string">v1</span></span><br><span class="line">  <span class="attr">k2:</span> <span class="string">v2</span></span><br><span class="line">  <span class="attr">k3:</span> <span class="string">v3</span></span><br></pre></td></tr></table></figure>

<ul>
<li>数组：一组按次序排列的值。array、list、queue</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">#行内写法：  </span></span><br><span class="line"><span class="attr">k:</span> [<span class="string">v1</span>,<span class="string">v2</span>,<span class="string">v3</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">#或</span></span><br><span class="line"><span class="attr">k:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v1</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v2</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v3</span></span><br></pre></td></tr></table></figure>

<h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> Boolean boss;</span><br><span class="line">    <span class="keyword">private</span> Date birth;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line">    <span class="keyword">private</span> String[] interests;</span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; animal;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Object&gt; score;</span><br><span class="line">    <span class="keyword">private</span> Set&lt;Double&gt; salarys;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, List&lt;Pet&gt;&gt; allPets;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pet</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Double weight;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>用 yaml 表示以上对象</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">person:</span></span><br><span class="line">  <span class="attr">userName:</span> <span class="string">zhangsan</span></span><br><span class="line">  <span class="attr">boss:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">birth:</span> <span class="number">2019</span><span class="string">/12/12</span> <span class="number">20</span><span class="string">:12:33</span></span><br><span class="line">  <span class="attr">age:</span> <span class="number">18</span></span><br><span class="line">  <span class="attr">pet:</span> </span><br><span class="line">    <span class="attr">name:</span> <span class="string">tomcat</span></span><br><span class="line">    <span class="attr">weight:</span> <span class="number">23.4</span></span><br><span class="line">  <span class="attr">interests:</span> [<span class="string">篮球</span>,<span class="string">游泳</span>]</span><br><span class="line">  <span class="attr">animal:</span> </span><br><span class="line">    <span class="bullet">-</span> <span class="string">jerry</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">mario</span></span><br><span class="line">  <span class="attr">score:</span></span><br><span class="line">    <span class="attr">english:</span> </span><br><span class="line">      <span class="attr">first:</span> <span class="number">30</span></span><br><span class="line">      <span class="attr">second:</span> <span class="number">40</span></span><br><span class="line">      <span class="attr">third:</span> <span class="number">50</span></span><br><span class="line">    <span class="attr">math:</span> [<span class="number">131</span>,<span class="number">140</span>,<span class="number">148</span>]</span><br><span class="line">    <span class="attr">chinese:</span> &#123;<span class="attr">first:</span> <span class="number">128</span>,<span class="attr">second:</span> <span class="number">136</span>&#125;</span><br><span class="line">  <span class="attr">salarys:</span> [<span class="number">3999</span>,<span class="number">4999.98</span>,<span class="number">5999.99</span>]</span><br><span class="line">  <span class="attr">allPets:</span></span><br><span class="line">    <span class="attr">sick:</span></span><br><span class="line">      <span class="bullet">-</span> &#123;<span class="attr">name:</span> <span class="string">tom</span>&#125;</span><br><span class="line">      <span class="bullet">-</span> &#123;<span class="attr">name:</span> <span class="string">jerry</span>,<span class="attr">weight:</span> <span class="number">47</span>&#125;</span><br><span class="line">    <span class="attr">health:</span> [&#123;<span class="attr">name:</span> <span class="string">mario</span>,<span class="attr">weight:</span> <span class="number">47</span>&#125;]</span><br></pre></td></tr></table></figure>

<h2 id="21、配置文件-自定义类绑定的配置提示"><a href="#21、配置文件-自定义类绑定的配置提示" class="headerlink" title="21、配置文件-自定义类绑定的配置提示"></a>21、配置文件-自定义类绑定的配置提示</h2><blockquote>
<p>You can easily generate your own configuration metadata file from items annotated with <code>@ConfigurationProperties</code> by using the <code>spring-boot-configuration-processor</code> jar. The jar includes a Java annotation processor which is invoked as your project is compiled.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.4.2/reference/htmlsingle/#configuration-metadata-annotation-processor">link</a></p>
</blockquote>
<p>自定义的类和配置文件绑定一般没有提示。若要提示，添加如下依赖：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 下面插件作用是工程打包时，不将spring-boot-configuration-processor打进包内，让其只在编码的时候有用 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 新版本已经不用手动配置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="22、web-场景-web-开发简介"><a href="#22、web-场景-web-开发简介" class="headerlink" title="22、web 场景-web 开发简介"></a>22、web 场景-web 开发简介</h2><p>Spring Boot provides auto-configuration for Spring MVC that <strong>works well with most applications.(大多场景我们都无需自定义配置)</strong></p>
<p>The auto-configuration adds the following features on top of Spring’s defaults:</p>
<ul>
<li><p>Inclusion of <code>ContentNegotiatingViewResolver</code> and <code>BeanNameViewResolver</code> beans.</p>
<ul>
<li>内容协商视图解析器和 BeanName 视图解析器</li>
</ul>
</li>
<li><p>Support for serving static resources, including support for WebJars (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-static-content">later in this document</a>)).</p>
<ul>
<li>静态资源（包括webjars）</li>
</ul>
</li>
<li><p>Automatic registration of <code>Converter</code>, <code>GenericConverter</code>, and <code>Formatter</code> beans.</p>
<ul>
<li>自动注册 <code>Converter，GenericConverter，Formatter </code></li>
</ul>
</li>
<li><p>Support for <code>HttpMessageConverters</code> (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-message-converters">later in this document</a>).</p>
<ul>
<li>支持 <code>HttpMessageConverters</code> （后来我们配合内容协商理解原理）</li>
</ul>
</li>
<li><p>Automatic registration of <code>MessageCodesResolver</code> (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-message-codes">later in this document</a>).</p>
<ul>
<li>自动注册 <code>MessageCodesResolver</code> （国际化用）</li>
</ul>
</li>
<li><p>Static <code>index.html</code> support.</p>
<ul>
<li>静态index.html 页支持</li>
</ul>
</li>
<li><p>Custom <code>Favicon</code> support (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-favicon">later in this document</a>).</p>
<ul>
<li>自定义 <code>Favicon</code></li>
</ul>
</li>
<li><p>Automatic use of a <code>ConfigurableWebBindingInitializer</code> bean (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-web-binding-initializer">later in this document</a>).</p>
<ul>
<li>自动使用 <code>ConfigurableWebBindingInitializer</code> ，（DataBinder 负责将请求数据绑定到 JavaBean上）</li>
</ul>
</li>
</ul>
<blockquote>
<p>If you want to keep those Spring Boot MVC customizations and make more <a target="_blank" rel="noopener" href="https://docs.spring.io/spring/docs/5.2.9.RELEASE/spring-framework-reference/web.html#mvc">MVC customizations</a> (interceptors, formatters, view controllers, and other features), you can add your own <code>@Configuration</code> class of type <code>WebMvcConfigurer</code> but <strong>without</strong> <code>@EnableWebMvc</code>.</p>
<p><strong>不用@EnableWebMvc注解。使用</strong> <strong><code>@Configuration</code></strong> <strong>+</strong> <strong><code>WebMvcConfigurer</code></strong> <strong>自定义规则</strong></p>
</blockquote>
<blockquote>
<p>If you want to provide custom instances of <code>RequestMappingHandlerMapping</code>, <code>RequestMappingHandlerAdapter</code>, or <code>ExceptionHandlerExceptionResolver</code>, and still keep the Spring Boot MVC customizations, you can declare a bean of type <code>WebMvcRegistrations</code> and use it to provide custom instances of those components.</p>
<p><strong>声明</strong> <strong><code>WebMvcRegistrations</code></strong> <strong>改变默认底层组件</strong></p>
</blockquote>
<blockquote>
<p>If you want to take complete control of Spring MVC, you can add your own <code>@Configuration</code> annotated with <code>@EnableWebMvc</code>, or alternatively add your own <code>@Configuration</code>-annotated <code>DelegatingWebMvcConfiguration</code> as described in the Javadoc of <code>@EnableWebMvc</code>.</p>
<p><strong>使用</strong> <strong><code>@EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管 SpringMVC</code></strong></p>
</blockquote>
<h2 id="23、web场景-静态资源规则与定制化"><a href="#23、web场景-静态资源规则与定制化" class="headerlink" title="23、web场景-静态资源规则与定制化"></a>23、web场景-静态资源规则与定制化</h2><h3 id="静态资源目录"><a href="#静态资源目录" class="headerlink" title="静态资源目录"></a>静态资源目录</h3><p>只要静态资源放在类路径下： called <code>/static</code> (or <code>/public</code> or <code>/resources</code> or <code>/META-INF/resources</code></p>
<p>访问 ： <code>当前项目根路径/ + 静态资源名</code></p>
<p>原理： 静态映射 <code>/**</code></p>
<p>请求进来，先去找 Controller 看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面</p>
<p>也可以改变默认的静态资源路径，<code>/static</code>，<code>/public</code>,<code>/resources</code>, <code>/META-INF/resources</code>失效</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">resources:</span></span><br><span class="line">  <span class="attr">static-locations:</span> [<span class="string">classpath:/haha/</span>]</span><br></pre></td></tr></table></figure>

<h3 id="静态资源访问前缀"><a href="#静态资源访问前缀" class="headerlink" title="静态资源访问前缀"></a>静态资源访问前缀</h3><p>默认无前缀，手动配置前缀如下</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">static-path-pattern:</span> <span class="string">/res/**</span></span><br></pre></td></tr></table></figure>

<p>当前项目 + static-path-pattern + 静态资源名 &#x3D; 静态资源文件夹下找</p>
<h3 id="webjar"><a href="#webjar" class="headerlink" title="webjar"></a>webjar</h3><p>可用 jar 方式添加 css，js 等资源文件，</p>
<p><a target="_blank" rel="noopener" href="https://www.webjars.org/">https://www.webjars.org/</a></p>
<p>例如，添加 jquery</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.webjars<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jquery<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.5.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>访问地址：<a target="_blank" rel="noopener" href="http://localhost:8080/webjars/jquery/3.5.1/jquery.js">http://localhost:8080/webjars/<strong>jquery&#x2F;3.5.1&#x2F;jquery.js</strong></a>  后面地址要按照依赖里面的包路径</p>
<h2 id="24、web场景-welcome-与-favicon-功能"><a href="#24、web场景-welcome-与-favicon-功能" class="headerlink" title="24、web场景-welcome 与 favicon 功能"></a>24、web场景-welcome 与 favicon 功能</h2><p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-welcome-page">官方文档</a></p>
<h3 id="欢迎页支持"><a href="#欢迎页支持" class="headerlink" title="欢迎页支持"></a>欢迎页支持</h3><ul>
<li><p>静态资源路径下  index.html。</p>
<ul>
<li>可以配置静态资源路径</li>
<li>但是不可以配置静态资源的访问前缀。否则导致 <code>index.html</code> 不能被默认访问</li>
</ul>
</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="comment">#  mvc:</span></span><br><span class="line"><span class="comment">#    static-path-pattern: /res/**   这个会导致welcome page功能失效</span></span><br><span class="line">  <span class="attr">resources:</span></span><br><span class="line">    <span class="attr">static-locations:</span> [<span class="string">classpath:/haha/</span>]</span><br></pre></td></tr></table></figure>

<ul>
<li>controller 能处理 <code>/index</code></li>
</ul>
<h3 id="自定义Favicon"><a href="#自定义Favicon" class="headerlink" title="自定义Favicon"></a>自定义Favicon</h3><p>指网页标签上的小图标</p>
<p><code>favicon.ico</code> 放在静态资源目录下即可</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="comment">#  mvc:</span></span><br><span class="line"><span class="comment">#    static-path-pattern: /res/**   这个会导致 Favicon 功能失效</span></span><br></pre></td></tr></table></figure>

<h2 id="25、web场景-【源码分析】-静态资源原理"><a href="#25、web场景-【源码分析】-静态资源原理" class="headerlink" title="25、web场景-【源码分析】-静态资源原理"></a>25、web场景-【源码分析】-静态资源原理</h2><ul>
<li>SpringBoot 启动默认加载  <code>xxxAutoConfiguration</code> 类（自动配置类）</li>
<li>SpringMVC 功能的自动配置类 <code>WebMvcAutoConfiguration</code>，生效</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnWebApplication(type = Type.SERVLET)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class &#125;)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)</span></span><br><span class="line"><span class="meta">@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,</span></span><br><span class="line"><span class="meta">		ValidationAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>给容器中配置的内容：<ul>
<li>配置文件的相关属性的绑定：<code>WebMvcProperties -&gt; spring.mvc</code>、<code>ResourceProperties -&gt; spring.resources</code></li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@Import(EnableWebMvcConfiguration.class)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(&#123; WebMvcProperties.class, ResourceProperties.class &#125;)</span></span><br><span class="line"><span class="meta">@Order(0)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfigurationAdapter</span> <span class="keyword">implements</span> <span class="title class_">WebMvcConfigurer</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="配置类只有一个有参构造器"><a href="#配置类只有一个有参构造器" class="headerlink" title="配置类只有一个有参构造器"></a>配置类只有一个有参构造器</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 有参构造器所有参数的值都会从容器中确定</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">WebMvcAutoConfigurationAdapter</span><span class="params">(WebProperties webProperties, WebMvcProperties mvcProperties,</span></span><br><span class="line"><span class="params">		ListableBeanFactory beanFactory, ObjectProvider&lt;HttpMessageConverters&gt; messageConvertersProvider,</span></span><br><span class="line"><span class="params">		ObjectProvider&lt;ResourceHandlerRegistrationCustomizer&gt; resourceHandlerRegistrationCustomizerProvider,</span></span><br><span class="line"><span class="params">		ObjectProvider&lt;DispatcherServletPath&gt; dispatcherServletPath,</span></span><br><span class="line"><span class="params">		ObjectProvider&lt;ServletRegistrationBean&lt;?&gt;&gt; servletRegistrations)</span> &#123;</span><br><span class="line">	<span class="built_in">this</span>.mvcProperties = mvcProperties;</span><br><span class="line">	<span class="built_in">this</span>.beanFactory = beanFactory;</span><br><span class="line">	<span class="built_in">this</span>.messageConvertersProvider = messageConvertersProvider;</span><br><span class="line">	<span class="built_in">this</span>.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();</span><br><span class="line">	<span class="built_in">this</span>.dispatcherServletPath = dispatcherServletPath;</span><br><span class="line">	<span class="built_in">this</span>.servletRegistrations = servletRegistrations;</span><br><span class="line">	<span class="built_in">this</span>.mvcProperties.checkConfiguration();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>ResourceProperties resourceProperties 获取和 spring.resources 绑定的所有的值的对象</li>
<li>WebMvcProperties mvcProperties 获取和 spring.mvc 绑定的所有的值的对象</li>
<li>ListableBeanFactory beanFactory Spring 的 beanFactory</li>
<li>HttpMessageConverters 找到所有的 HttpMessageConverters</li>
<li>ResourceHandlerRegistrationCustomizer 找到资源处理器的自定义器</li>
<li>DispatcherServletPath</li>
<li>ServletRegistrationBean   给应用注册 Servlet、Filter….</li>
</ul>
<h3 id="资源处理的默认规则"><a href="#资源处理的默认规则" class="headerlink" title="资源处理的默认规则"></a>资源处理的默认规则</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EnableWebMvcConfiguration</span> <span class="keyword">extends</span> <span class="title class_">DelegatingWebMvcConfiguration</span> <span class="keyword">implements</span> <span class="title class_">ResourceLoaderAware</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> &#123;</span><br><span class="line">			<span class="built_in">super</span>.addResourceHandlers(registry);</span><br><span class="line">			<span class="keyword">if</span> (!<span class="built_in">this</span>.resourceProperties.isAddMappings()) &#123;</span><br><span class="line">				logger.debug(<span class="string">&quot;Default resource handling disabled&quot;</span>);</span><br><span class="line">				<span class="keyword">return</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="type">ServletContext</span> <span class="variable">servletContext</span> <span class="operator">=</span> getServletContext();</span><br><span class="line">			addResourceHandler(registry, <span class="string">&quot;/webjars/**&quot;</span>, <span class="string">&quot;classpath:/META-INF/resources/webjars/&quot;</span>);</span><br><span class="line">			addResourceHandler(registry, <span class="built_in">this</span>.mvcProperties.getStaticPathPattern(), (registration) -&gt; &#123;</span><br><span class="line">				registration.addResourceLocations(<span class="built_in">this</span>.resourceProperties.getStaticLocations());</span><br><span class="line">				<span class="keyword">if</span> (servletContext != <span class="literal">null</span>) &#123;</span><br><span class="line">					registration.addResourceLocations(<span class="keyword">new</span> <span class="title class_">ServletContextResource</span>(servletContext, SERVLET_LOCATION));</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;);</span><br><span class="line">		&#125;</span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>根据上述代码，我们可以同过配置禁止所有静态资源规则</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">resources:</span></span><br><span class="line">    <span class="attr">add-mappings:</span> <span class="literal">false</span>   <span class="comment">#禁用所有静态资源规则</span></span><br></pre></td></tr></table></figure>

<p>静态资源规则：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;spring.resources&quot;, ignoreUnknownFields = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResourceProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] CLASSPATH_RESOURCE_LOCATIONS = &#123; <span class="string">&quot;classpath:/META-INF/resources/&quot;</span>,</span><br><span class="line">            <span class="string">&quot;classpath:/resources/&quot;</span>, <span class="string">&quot;classpath:/static/&quot;</span>, <span class="string">&quot;classpath:/public/&quot;</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,</span></span><br><span class="line"><span class="comment">     * /resources/, /static/, /public/].</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="欢迎页的处理规则"><a href="#欢迎页的处理规则" class="headerlink" title="欢迎页的处理规则"></a>欢迎页的处理规则</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EnableWebMvcConfiguration</span> <span class="keyword">extends</span> <span class="title class_">DelegatingWebMvcConfiguration</span> <span class="keyword">implements</span> <span class="title class_">ResourceLoaderAware</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">		<span class="meta">@Bean</span></span><br><span class="line">		<span class="keyword">public</span> WelcomePageHandlerMapping <span class="title function_">welcomePageHandlerMapping</span><span class="params">(ApplicationContext applicationContext,</span></span><br><span class="line"><span class="params">				FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider)</span> &#123;</span><br><span class="line">			<span class="type">WelcomePageHandlerMapping</span> <span class="variable">welcomePageHandlerMapping</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WelcomePageHandlerMapping</span>(</span><br><span class="line">					<span class="keyword">new</span> <span class="title class_">TemplateAvailabilityProviders</span>(applicationContext), applicationContext, getWelcomePage(),</span><br><span class="line">					<span class="built_in">this</span>.mvcProperties.getStaticPathPattern());</span><br><span class="line">			welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));</span><br><span class="line">			welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());</span><br><span class="line">			<span class="keyword">return</span> welcomePageHandlerMapping;</span><br><span class="line">		&#125;</span><br><span class="line">    </span><br></pre></td></tr></table></figure>

<p><code>WelcomePageHandlerMapping</code> 的构造方法如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,</span><br><span class="line">                          ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) &#123;</span><br><span class="line">    <span class="keyword">if</span> (welcomePage != <span class="literal">null</span> &amp;&amp; <span class="string">&quot;/**&quot;</span>.equals(staticPathPattern)) &#123;</span><br><span class="line">        <span class="comment">//要用欢迎页功能，必须是/**</span></span><br><span class="line">        logger.info(<span class="string">&quot;Adding welcome page: &quot;</span> + welcomePage);</span><br><span class="line">        setRootViewName(<span class="string">&quot;forward:index.html&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) &#123;</span><br><span class="line">        <span class="comment">//调用Controller /index</span></span><br><span class="line">        logger.info(<span class="string">&quot;Adding welcome page template: index&quot;</span>);</span><br><span class="line">        setRootViewName(<span class="string">&quot;index&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这构造方法内的代码也解释了 <a href="#">web场景-welcome与favicon功能</a> 中配置<code>static-path-pattern</code>了，welcome页面和小图标失效的问题</p>
<h2 id="26、请求处理-【源码分析】-Rest映射及源码解析"><a href="#26、请求处理-【源码分析】-Rest映射及源码解析" class="headerlink" title="26、请求处理-【源码分析】-Rest映射及源码解析"></a>26、请求处理-【源码分析】-Rest映射及源码解析</h2><h3 id="请求映射"><a href="#请求映射" class="headerlink" title="请求映射"></a>请求映射</h3><ul>
<li><p>@xxxMapping;</p>
<ul>
<li>@GetMapping</li>
<li>@PostMapping</li>
<li>@PutMapping</li>
<li>@DeleteMapping</li>
</ul>
</li>
<li><p>Rest风格支持（使用<strong>HTTP</strong>请求方式动词来表示对资源的操作）</p>
<ul>
<li>以前：<ul>
<li>&#x2F;getUser 获取用户</li>
<li>&#x2F;deleteUser 删除用户</li>
<li>&#x2F;editUser 修改用户</li>
<li>&#x2F;saveUser保存用户</li>
</ul>
</li>
<li>现在： &#x2F;user <ul>
<li>GET-获取用户</li>
<li>DELETE-删除用户</li>
<li>PUT-修改用户</li>
<li>POST-保存用户</li>
</ul>
</li>
<li>核心 Filter：<code>HiddenHttpMethodFilter</code></li>
</ul>
</li>
<li><p><strong>用法</strong></p>
<ul>
<li>开启页面表单的 Rest 功能</li>
<li>页面 form 的属性 method&#x3D;post，隐藏域 _method&#x3D;put、delete等（如果直接 get 或 post，无需隐藏域）</li>
<li>编写请求映射</li>
</ul>
</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">hiddenmethod:</span></span><br><span class="line">      <span class="attr">filter:</span></span><br><span class="line">        <span class="attr">enabled:</span> <span class="literal">true</span>   <span class="comment">#开启页面表单的Rest功能</span></span><br></pre></td></tr></table></figure>

<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;get&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-GET提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-POST提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_method&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;DELETE&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-DELETE 提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_method&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;PUT&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-PUT提交&quot;</span><span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.GET)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;GET-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PostMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.POST)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">saveUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;POST-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PutMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.PUT)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">putUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;PUT-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@DeleteMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">deleteUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;DELETE-张三&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>Rest原理（表单提交要使用REST的时候）<ul>
<li>表单提交会带上 <code>\_method=PUT</code></li>
<li>请求过来被 <code>HiddenHttpMethodFilter </code>拦截<ul>
<li>请求是否正常，并且是 POST<ul>
<li>获取到 <code>\_method</code> 的值</li>
<li>兼容以下请求；<strong>PUT</strong>.<strong>DELETE</strong>.<strong>PATCH</strong></li>
<li><strong>原生 request（post），包装模式 requesWrapper 重写了 getMethod 方法，返回的是传入的值</strong></li>
<li><strong>过滤器链放行的时候用 wrapper。以后的方法调用 getMethod 是调用 requesWrapper 的</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HiddenHttpMethodFilter</span> <span class="keyword">extends</span> <span class="title class_">OncePerRequestFilter</span> &#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> List&lt;String&gt; ALLOWED_METHODS =</span><br><span class="line">			Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),</span><br><span class="line">					HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));</span><br><span class="line"></span><br><span class="line">	<span class="comment">/** Default method parameter: &#123;<span class="doctag">@code</span> _method&#125;. */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_METHOD_PARAM</span> <span class="operator">=</span> <span class="string">&quot;_method&quot;</span>;</span><br><span class="line">	<span class="keyword">private</span> <span class="type">String</span> <span class="variable">methodParam</span> <span class="operator">=</span> DEFAULT_METHOD_PARAM;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Set the parameter name to look for HTTP methods.</span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@see</span> #DEFAULT_METHOD_PARAM</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMethodParam</span><span class="params">(String methodParam)</span> &#123;</span><br><span class="line">		Assert.hasText(methodParam, <span class="string">&quot;&#x27;methodParam&#x27; must not be empty&quot;</span>);</span><br><span class="line">		<span class="built_in">this</span>.methodParam = methodParam;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)</span></span><br><span class="line">			<span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">HttpServletRequest</span> <span class="variable">requestToUse</span> <span class="operator">=</span> request;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (<span class="string">&quot;POST&quot;</span>.equals(request.getMethod()) &amp;&amp; request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="type">String</span> <span class="variable">paramValue</span> <span class="operator">=</span> request.getParameter(<span class="built_in">this</span>.methodParam);</span><br><span class="line">			<span class="keyword">if</span> (StringUtils.hasLength(paramValue)) &#123;</span><br><span class="line">				<span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> paramValue.toUpperCase(Locale.ENGLISH);</span><br><span class="line">				<span class="keyword">if</span> (ALLOWED_METHODS.contains(method)) &#123;</span><br><span class="line">					requestToUse = <span class="keyword">new</span> <span class="title class_">HttpMethodRequestWrapper</span>(request, method);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		filterChain.doFilter(requestToUse, response);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Simple &#123;<span class="doctag">@link</span> HttpServletRequest&#125; wrapper that returns the supplied method for</span></span><br><span class="line"><span class="comment">	 * &#123;<span class="doctag">@link</span> HttpServletRequest#getMethod()&#125;.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">HttpMethodRequestWrapper</span> <span class="keyword">extends</span> <span class="title class_">HttpServletRequestWrapper</span> &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">private</span> <span class="keyword">final</span> String method;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">public</span> <span class="title function_">HttpMethodRequestWrapper</span><span class="params">(HttpServletRequest request, String method)</span> &#123;</span><br><span class="line">			<span class="built_in">super</span>(request);</span><br><span class="line">			<span class="built_in">this</span>.method = method;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="keyword">public</span> String <span class="title function_">getMethod</span><span class="params">()</span> &#123;</span><br><span class="line">			<span class="keyword">return</span> <span class="built_in">this</span>.method;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>Rest 使用客户端工具。<ul>
<li>如 PostMan 可直接发送 put、delete 等方式请求</li>
</ul>
</li>
</ul>
<h2 id="27、请求处理-【源码分析】-怎么改变默认的-method"><a href="#27、请求处理-【源码分析】-怎么改变默认的-method" class="headerlink" title="27、请求处理-【源码分析】-怎么改变默认的_method"></a>27、请求处理-【源码分析】-怎么改变默认的_method</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnWebApplication(type = Type.SERVLET)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class &#125;)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)</span></span><br><span class="line"><span class="meta">@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,</span></span><br><span class="line"><span class="meta">		ValidationAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty(prefix = &quot;spring.mvc.hiddenmethod.filter&quot;, name = &quot;enabled&quot;, matchIfMissing = false)</span></span><br><span class="line">    <span class="keyword">public</span> OrderedHiddenHttpMethodFilter <span class="title function_">hiddenHttpMethodFilter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">OrderedHiddenHttpMethodFilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line">    </span><br></pre></td></tr></table></figure>

<p><code>@ConditionalOnMissingBean(HiddenHttpMethodFilter.class) </code>意味着在没有 <code>HiddenHttpMethodFilter</code>时，才执行 <code>hiddenHttpMethodFilter()</code>。因此，我们可以自定义 filter，改变默认的 <code>\_method</code>。例如：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span>&#123;</span><br><span class="line">    <span class="comment">//自定义filter</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> HiddenHttpMethodFilter <span class="title function_">hiddenHttpMethodFilter</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">HiddenHttpMethodFilter</span> <span class="variable">methodFilter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiddenHttpMethodFilter</span>();</span><br><span class="line">        methodFilter.setMethodParam(<span class="string">&quot;_m&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> methodFilter;</span><br><span class="line">    &#125;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>将 <code>\_method</code> 改成 <code>_m</code></p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_m&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;DELETE&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-DELETE 提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="28、请求处理-【源码分析】-请求映射原理"><a href="#28、请求处理-【源码分析】-请求映射原理" class="headerlink" title="28、请求处理-【源码分析】-请求映射原理"></a>28、请求处理-【源码分析】-请求映射原理</h2><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005703527.png" alt="在这里插入图片描述"><br>SpringMVC 功能分析都从 <code>org.springframework.web.servlet.DispatcherServlet</code> -&gt; <code>doDispatch()</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">HttpServletRequest</span> <span class="variable">processedRequest</span> <span class="operator">=</span> request;</span><br><span class="line">    <span class="type">HandlerExecutionChain</span> <span class="variable">mappedHandler</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">multipartRequestParsed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">WebAsyncManager</span> <span class="variable">asyncManager</span> <span class="operator">=</span> WebAsyncUtils.getAsyncManager(request);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">Exception</span> <span class="variable">dispatchException</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            processedRequest = checkMultipart(request);</span><br><span class="line">            multipartRequestParsed = (processedRequest != request);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 找到当前请求使用哪个Handler（Controller的方法）处理</span></span><br><span class="line">            mappedHandler = getHandler(processedRequest);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//HandlerMapping：处理器映射。/xxx-&gt;&gt;xxxx</span></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>getHandler()</code>方法如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> HandlerExecutionChain <span class="title function_">getHandler</span><span class="params">(HttpServletRequest request)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.handlerMappings != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (HandlerMapping mapping : <span class="built_in">this</span>.handlerMappings) &#123;</span><br><span class="line">            <span class="type">HandlerExecutionChain</span> <span class="variable">handler</span> <span class="operator">=</span> mapping.getHandler(request);</span><br><span class="line">            <span class="keyword">if</span> (handler != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> handler;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>this.handlerMappings</code>在Debug模式下展现的内容：</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005802305.png" alt="在这里插入图片描述"><br>其中，保存了所有<code>@RequestMapping</code> 和<code>handler</code>的映射规则</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205005926474.png" alt="在这里插入图片描述"></p>
<p>所有的请求映射都在 HandlerMapping 中：</p>
<ul>
<li><p>SpringBoot 自动配置欢迎页的 WelcomePageHandlerMapping 。访问 <code>/</code> 能访问到 <code>index.html</code></p>
</li>
<li><p>SpringBoot 自动配置了默认的 RequestMappingHandlerMapping</p>
</li>
<li><p>请求进来，挨个尝试所有的 HandlerMapping 看是否有请求信息</p>
<ul>
<li>如果有就找到这个请求对应的 handler</li>
<li>如果没有就是下一个 HandlerMapping</li>
</ul>
</li>
<li><p>我们需要一些自定义的映射处理，我们也可以自己给容器中放 <strong>HandlerMapping</strong>，自定义 <strong>HandlerMapping</strong></p>
</li>
</ul>
<hr>
<p>IDEA快捷键：</p>
<ul>
<li>Ctrl + Alt + U : 以UML的类图展现类有哪些继承类，派生类以及实现哪些接口。</li>
<li>Crtl + Alt + Shift + U : 同上，区别在于上条快捷键结果在新页展现，而本条快捷键结果在弹窗展现。</li>
<li>Ctrl + H : 以树形方式展现类层次结构图。</li>
</ul>
<h2 id="29、请求处理-常用参数注解使用"><a href="#29、请求处理-常用参数注解使用" class="headerlink" title="29、请求处理-常用参数注解使用"></a>29、请求处理-常用参数注解使用</h2><p>注解：</p>
<ul>
<li><code>@PathVariable</code> 路径变量</li>
<li><code>@RequestHeader</code> 获取请求头</li>
<li><code>@RequestParam</code> 获取请求参数（指问号后的参数，url?a&#x3D;1&amp;b&#x3D;2）</li>
<li><code>@CookieValue</code> 获取Cookie值</li>
<li><code>@RequestAttribute</code> 获取request域属性</li>
<li><code>@RequestBody</code> 获取请求体[POST]</li>
<li><code>@MatrixVariable</code> 矩阵变量</li>
<li><code>@ModelAttribute</code></li>
</ul>
<p>使用用例：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//  car/2/owner/zhangsan</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/car/&#123;id&#125;/owner/&#123;username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String,Object&gt; <span class="title function_">getCar</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer id,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@PathVariable(&quot;username&quot;)</span> String name,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@PathVariable</span> Map&lt;String,String&gt; pv,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestHeader(&quot;User-Agent&quot;)</span> String userAgent,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestHeader</span> Map&lt;String,String&gt; header,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam(&quot;age&quot;)</span> Integer age,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam(&quot;inters&quot;)</span> List&lt;String&gt; inters,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam</span> Map&lt;String,String&gt; params,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@CookieValue(&quot;_ga&quot;)</span> String _ga,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@CookieValue(&quot;_ga&quot;)</span> Cookie cookie)</span>&#123;</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">//        map.put(&quot;id&quot;,id);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;name&quot;,name);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;pv&quot;,pv);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;userAgent&quot;,userAgent);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;headers&quot;,header);</span></span><br><span class="line">        map.put(<span class="string">&quot;age&quot;</span>,age);</span><br><span class="line">        map.put(<span class="string">&quot;inters&quot;</span>,inters);</span><br><span class="line">        map.put(<span class="string">&quot;params&quot;</span>,params);</span><br><span class="line">        map.put(<span class="string">&quot;_ga&quot;</span>,_ga);</span><br><span class="line">        System.out.println(cookie.getName()+<span class="string">&quot;===&gt;&quot;</span>+cookie.getValue());</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">postMethod</span><span class="params">(<span class="meta">@RequestBody</span> String content)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;content&quot;</span>,content);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="30、请求处理-RequestAttribute"><a href="#30、请求处理-RequestAttribute" class="headerlink" title="30、请求处理-@RequestAttribute"></a>30、请求处理-@RequestAttribute</h2><p>用例：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/goto&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">goToPage</span><span class="params">(HttpServletRequest request)</span>&#123;</span><br><span class="line">        request.setAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;成功了...&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;code&quot;</span>,<span class="number">200</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;  <span class="comment">//转发到  /success请求</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/params&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">testParam</span><span class="params">(Map&lt;String,Object&gt; map,</span></span><br><span class="line"><span class="params">                            Model model,</span></span><br><span class="line"><span class="params">                            HttpServletRequest request,</span></span><br><span class="line"><span class="params">                            HttpServletResponse response)</span>&#123;</span><br><span class="line">        map.put(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world666&quot;</span>);</span><br><span class="line">        model.addAttribute(<span class="string">&quot;world&quot;</span>,<span class="string">&quot;hello666&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;message&quot;</span>,<span class="string">&quot;HelloWorld&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Cookie</span> <span class="variable">cookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;c1&quot;</span>,<span class="string">&quot;v1&quot;</span>);</span><br><span class="line">        response.addCookie(cookie);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">///&lt;-----------------主角@RequestAttribute在这个方法</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/success&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">success</span><span class="params">(<span class="meta">@RequestAttribute(value = &quot;msg&quot;,required = false)</span> String msg,</span></span><br><span class="line"><span class="params">                       <span class="meta">@RequestAttribute(value = &quot;code&quot;,required = false)</span>Integer code,</span></span><br><span class="line"><span class="params">                       HttpServletRequest request)</span>&#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">msg1</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;msg&quot;</span>);</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">hello</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">world</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;world&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">message</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;message&quot;</span>);</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;reqMethod_msg&quot;</span>,msg1);</span><br><span class="line">        map.put(<span class="string">&quot;annotation_msg&quot;</span>,msg);</span><br><span class="line">        map.put(<span class="string">&quot;hello&quot;</span>,hello);</span><br><span class="line">        map.put(<span class="string">&quot;world&quot;</span>,world);</span><br><span class="line">        map.put(<span class="string">&quot;message&quot;</span>,message);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="31、请求处理-MatrixVariable-与-UrlPathHelper"><a href="#31、请求处理-MatrixVariable-与-UrlPathHelper" class="headerlink" title="31、请求处理-@MatrixVariable 与 UrlPathHelper"></a>31、请求处理-@MatrixVariable 与 UrlPathHelper</h2><ol>
<li><p>语法： 请求路径：<code>/cars/sell;low=34;brand=byd,audi,yd</code></p>
</li>
<li><p>SpringBoot 默认是禁用了矩阵变量的功能</p>
<ul>
<li>手动开启：对于路径的处理。UrlPathHelper的removeSemicolonContent 设置为 false，让其支持矩阵变量的</li>
</ul>
</li>
<li><p>矩阵变量<strong>必须</strong>有 url 路径变量才能被解析</p>
</li>
</ol>
<p><strong>手动开启矩阵变量</strong>：</p>
<ul>
<li>实现 <code>WebMvcConfigurer</code> 接口：</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> <span class="keyword">implements</span> <span class="title class_">WebMvcConfigurer</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configurePathMatch</span><span class="params">(PathMatchConfigurer configurer)</span> &#123;</span><br><span class="line">        <span class="type">UrlPathHelper</span> <span class="variable">urlPathHelper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlPathHelper</span>();</span><br><span class="line">        <span class="comment">// 不移除；后面的内容。矩阵变量功能就可以生效</span></span><br><span class="line">        urlPathHelper.setRemoveSemicolonContent(<span class="literal">false</span>);</span><br><span class="line">        configurer.setUrlPathHelper(urlPathHelper);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>创建返回 <code>WebMvcConfigurer</code> Bean：</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configurePathMatch</span><span class="params">(PathMatchConfigurer configurer)</span> &#123;</span><br><span class="line">                <span class="type">UrlPathHelper</span> <span class="variable">urlPathHelper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlPathHelper</span>();</span><br><span class="line">                <span class="comment">// 不移除；后面的内容。矩阵变量功能就可以生效</span></span><br><span class="line">                urlPathHelper.setRemoveSemicolonContent(<span class="literal">false</span>);</span><br><span class="line">                configurer.setUrlPathHelper(urlPathHelper);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong><code>@MatrixVariable</code>的用例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">///cars/sell;low=34;brand=byd,audi,yd</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/cars/&#123;path&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">carsSell</span><span class="params">(<span class="meta">@MatrixVariable(&quot;low&quot;)</span> Integer low,</span></span><br><span class="line"><span class="params">                        <span class="meta">@MatrixVariable(&quot;brand&quot;)</span> List&lt;String&gt; brand,</span></span><br><span class="line"><span class="params">                        <span class="meta">@PathVariable(&quot;path&quot;)</span> String path)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;low&quot;</span>,low);</span><br><span class="line">        map.put(<span class="string">&quot;brand&quot;</span>,brand);</span><br><span class="line">        map.put(<span class="string">&quot;path&quot;</span>,path);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// /boss/1;age=20/2;age=10</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/boss/&#123;bossId&#125;/&#123;empId&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">boss</span><span class="params">(<span class="meta">@MatrixVariable(value = &quot;age&quot;,pathVar = &quot;bossId&quot;)</span> Integer bossAge,</span></span><br><span class="line"><span class="params">                    <span class="meta">@MatrixVariable(value = &quot;age&quot;,pathVar = &quot;empId&quot;)</span> Integer empAge)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;bossAge&quot;</span>,bossAge);</span><br><span class="line">        map.put(<span class="string">&quot;empAge&quot;</span>,empAge);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="32、请求处理-【源码分析】-各种类型参数解析原理"><a href="#32、请求处理-【源码分析】-各种类型参数解析原理" class="headerlink" title="32、请求处理-【源码分析】-各种类型参数解析原理"></a>32、请求处理-【源码分析】-各种类型参数解析原理</h2><p>这要从<code>DispatcherServlet</code>开始说起：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">HttpServletRequest</span> <span class="variable">processedRequest</span> <span class="operator">=</span> request;</span><br><span class="line">        <span class="type">HandlerExecutionChain</span> <span class="variable">mappedHandler</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">multipartRequestParsed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">WebAsyncManager</span> <span class="variable">asyncManager</span> <span class="operator">=</span> WebAsyncUtils.getAsyncManager(request);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">Exception</span> <span class="variable">dispatchException</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                processedRequest = checkMultipart(request);</span><br><span class="line">                multipartRequestParsed = (processedRequest != request);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Determine handler for the current request.</span></span><br><span class="line">                mappedHandler = getHandler(processedRequest);</span><br><span class="line">                <span class="keyword">if</span> (mappedHandler == <span class="literal">null</span>) &#123;</span><br><span class="line">                    noHandlerFound(processedRequest, response);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Determine handler adapter for the current request.</span></span><br><span class="line">                <span class="type">HandlerAdapter</span> <span class="variable">ha</span> <span class="operator">=</span> getHandlerAdapter(mappedHandler.getHandler());</span><br><span class="line">                ...</span><br></pre></td></tr></table></figure>

<ul>
<li><code>HandlerMapping</code> 中找到能处理请求的 <code>Handler</code>（Controller.method()）</li>
<li>为当前 Handler 找一个适配器 <code>HandlerAdapter</code>，用的最多的是 <strong>RequestMappingHandlerAdapter</strong></li>
<li>适配器执行目标方法并确定方法参数的每一个值</li>
</ul>
<h3 id="HandlerAdapter"><a href="#HandlerAdapter" class="headerlink" title="HandlerAdapter"></a>HandlerAdapter</h3><p>默认会加载所有 <code>HandlerAdapter</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    <span class="comment">/** Detect all HandlerAdapters or just expect &quot;handlerAdapter&quot; bean?. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">detectAllHandlerAdapters</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initHandlerAdapters</span><span class="params">(ApplicationContext context)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.handlerAdapters = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.detectAllHandlerAdapters) &#123;</span><br><span class="line">            <span class="comment">// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.</span></span><br><span class="line">            Map&lt;String, HandlerAdapter&gt; matchingBeans =</span><br><span class="line">                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (!matchingBeans.isEmpty()) &#123;</span><br><span class="line">                <span class="built_in">this</span>.handlerAdapters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(matchingBeans.values());</span><br><span class="line">                <span class="comment">// We keep HandlerAdapters in sorted order.</span></span><br><span class="line">                AnnotationAwareOrderComparator.sort(<span class="built_in">this</span>.handlerAdapters);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">     ...</span><br></pre></td></tr></table></figure>

<p>有这些<code>HandlerAdapter</code>：</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205010047654.png" alt="在这里插入图片描述"></p>
<ol start="0">
<li><p>支持方法上标注 <code>@RequestMapping</code> </p>
</li>
<li><p>支持函数式编程的</p>
</li>
<li><p>…</p>
</li>
<li><p>…</p>
</li>
</ol>
<h3 id="执行目标方法"><a href="#执行目标方法" class="headerlink" title="执行目标方法"></a>执行目标方法</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine handler for the current request.</span></span><br><span class="line">        mappedHandler = getHandler(processedRequest);</span><br><span class="line">        <span class="keyword">if</span> (mappedHandler == <span class="literal">null</span>) &#123;</span><br><span class="line">            noHandlerFound(processedRequest, response);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine handler adapter for the current request.</span></span><br><span class="line">        <span class="type">HandlerAdapter</span> <span class="variable">ha</span> <span class="operator">=</span> getHandlerAdapter(mappedHandler.getHandler());</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">		<span class="comment">//本节重点</span></span><br><span class="line">        <span class="comment">// Actually invoke the handler.</span></span><br><span class="line">        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br></pre></td></tr></table></figure>

<p><code>HandlerAdapter</code>接口实现类<code>RequestMappingHandlerAdapter</code>（主要用来处理<code>@RequestMapping</code>）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//AbstractHandlerMethodAdapter类的方法，RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">        <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    	ModelAndView mav;</span><br><span class="line">        <span class="comment">//handleInternal的核心</span></span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);<span class="comment">//解释看下节</span></span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">		<span class="keyword">return</span> mav;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="参数解析器"><a href="#参数解析器" class="headerlink" title="参数解析器"></a>参数解析器</h3><p>确定将要执行的目标方法的每一个参数的值是什么</p>
<p>SpringMVC 目标方法能写多少种参数类型。取决于<strong>参数解析器 argumentResolvers</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                           HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">        <span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;-----关注点</span></span><br><span class="line">            invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        ...</span><br></pre></td></tr></table></figure>

<p><code>this.argumentResolvers</code>在<code>afterPropertiesSet()</code>方法内初始化</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line">	</span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodArgumentResolverComposite argumentResolvers;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">    	<span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers == <span class="literal">null</span>) &#123;<span class="comment">//初始化argumentResolvers</span></span><br><span class="line">        	List&lt;HandlerMethodArgumentResolver&gt; resolvers = getDefaultArgumentResolvers();</span><br><span class="line">            <span class="built_in">this</span>.argumentResolvers = <span class="keyword">new</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span>().addResolvers(resolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化了一堆的实现HandlerMethodArgumentResolver接口的</span></span><br><span class="line">	<span class="keyword">private</span> List&lt;HandlerMethodArgumentResolver&gt; <span class="title function_">getDefaultArgumentResolvers</span><span class="params">()</span> &#123;</span><br><span class="line">		List&lt;HandlerMethodArgumentResolver&gt; resolvers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">30</span>);</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Annotation-based argument resolution</span></span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMethodArgumentResolver</span>(getBeanFactory(), <span class="literal">false</span>));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMapMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">PathVariableMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">PathVariableMapMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">MatrixVariableMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">MatrixVariableMapMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">false</span>));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestPartMethodArgumentResolver</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestHeaderMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestHeaderMapMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletCookieValueMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ExpressionValueMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">SessionAttributeMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestAttributeMethodArgumentResolver</span>());</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Type-based argument resolution</span></span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletRequestMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletResponseMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">HttpEntityMethodProcessor</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RedirectAttributesMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ModelMethodProcessor</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">MapMethodProcessor</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ErrorsMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">SessionStatusMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">UriComponentsBuilderMethodArgumentResolver</span>());</span><br><span class="line">		<span class="keyword">if</span> (KotlinDetector.isKotlinPresent()) &#123;</span><br><span class="line">			resolvers.add(<span class="keyword">new</span> <span class="title class_">ContinuationHandlerMethodArgumentResolver</span>());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Custom arguments</span></span><br><span class="line">		<span class="keyword">if</span> (getCustomArgumentResolvers() != <span class="literal">null</span>) &#123;</span><br><span class="line">			resolvers.addAll(getCustomArgumentResolvers());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Catch-all</span></span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">PrincipalMethodArgumentResolver</span>());</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMethodArgumentResolver</span>(getBeanFactory(), <span class="literal">true</span>));</span><br><span class="line">		resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">true</span>));</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> resolvers;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>HandlerMethodArgumentResolverComposite</code>类如下：（众多<strong>参数解析器argumentResolvers</strong>的包装类）。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> List&lt;HandlerMethodArgumentResolver&gt; argumentResolvers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">public</span> HandlerMethodArgumentResolverComposite <span class="title function_">addResolvers</span><span class="params">(</span></span><br><span class="line"><span class="params">			<span class="meta">@Nullable</span> HandlerMethodArgumentResolver... resolvers)</span> &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (resolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">			Collections.addAll(<span class="built_in">this</span>.argumentResolvers, resolvers);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们看看<code>HandlerMethodArgumentResolver</code>的源码：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line">    <span class="comment">//当前解析器是否支持解析这种参数</span></span><br><span class="line">	<span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span><span class="comment">//如果支持，就调用 resolveArgument</span></span><br><span class="line">	Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="返回值处理器"><a href="#返回值处理器" class="headerlink" title="返回值处理器"></a>返回值处理器</h3><p><strong>ValueHandler</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                           HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">        <span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">            invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;---关注点</span></span><br><span class="line">            invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">        &#125;</span><br><span class="line">     ...</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><code>this.returnValueHandlers</code>在<code>afterPropertiesSet()</code>方法内初始化</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line">	</span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> HandlerMethodReturnValueHandlerComposite returnValueHandlers;</span><br><span class="line">    </span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">		<span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers == <span class="literal">null</span>) &#123;</span><br><span class="line">			List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">			<span class="built_in">this</span>.returnValueHandlers = <span class="keyword">new</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span>().addHandlers(handlers);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//初始化了一堆的实现HandlerMethodReturnValueHandler接口的</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;HandlerMethodReturnValueHandler&gt; <span class="title function_">getDefaultReturnValueHandlers</span><span class="params">()</span> &#123;</span><br><span class="line">		List&lt;HandlerMethodReturnValueHandler&gt; handlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Single-purpose return value types</span></span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ModelAndViewMethodReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ModelMethodProcessor</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ViewMethodReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ResponseBodyEmitterReturnValueHandler</span>(getMessageConverters(),</span><br><span class="line">				<span class="built_in">this</span>.reactiveAdapterRegistry, <span class="built_in">this</span>.taskExecutor, <span class="built_in">this</span>.contentNegotiationManager));</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">StreamingResponseBodyReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">HttpEntityMethodProcessor</span>(getMessageConverters(),</span><br><span class="line">				<span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">HttpHeadersReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">CallableMethodReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">DeferredResultMethodReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">AsyncTaskMethodReturnValueHandler</span>(<span class="built_in">this</span>.beanFactory));</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Annotation-based return value types</span></span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">false</span>));</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(),</span><br><span class="line">				<span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Multi-purpose return value types</span></span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">ViewNameMethodReturnValueHandler</span>());</span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">MapMethodProcessor</span>());</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Custom return value types</span></span><br><span class="line">		<span class="keyword">if</span> (getCustomReturnValueHandlers() != <span class="literal">null</span>) &#123;</span><br><span class="line">			handlers.addAll(getCustomReturnValueHandlers());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Catch-all</span></span><br><span class="line">		<span class="keyword">if</span> (!CollectionUtils.isEmpty(getModelAndViewResolvers())) &#123;</span><br><span class="line">			handlers.add(<span class="keyword">new</span> <span class="title class_">ModelAndViewResolverMethodReturnValueHandler</span>(getModelAndViewResolvers()));</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			handlers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">true</span>));</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> handlers;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>HandlerMethodReturnValueHandlerComposite</code>类如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> List&lt;HandlerMethodReturnValueHandler&gt; returnValueHandlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">public</span> HandlerMethodReturnValueHandlerComposite <span class="title function_">addHandlers</span><span class="params">(</span></span><br><span class="line"><span class="params">			<span class="meta">@Nullable</span> List&lt;? extends HandlerMethodReturnValueHandler&gt; handlers)</span> &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (handlers != <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="built_in">this</span>.returnValueHandlers.addAll(handlers);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>HandlerMethodReturnValueHandler</code>接口：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="type">boolean</span> <span class="title function_">supportsReturnType</span><span class="params">(MethodParameter returnType)</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="回顾执行目标方法"><a href="#回顾执行目标方法" class="headerlink" title="回顾执行目标方法"></a>回顾执行目标方法</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">		...</span><br><span class="line">        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br></pre></td></tr></table></figure>

<p><code>RequestMappingHandlerAdapter</code>的<code>handle()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//AbstractHandlerMethodAdapter类的方法，RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">        <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    	ModelAndView mav;</span><br><span class="line">        <span class="comment">//handleInternal的核心</span></span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);<span class="comment">//解释看下节</span></span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">		<span class="keyword">return</span> mav;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>RequestMappingHandlerAdapter</code>的<code>invokeHandlerMethod()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			...</span><br><span class="line">            </span><br><span class="line">            <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">				invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;</span><br><span class="line">				invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">			&#125;</span><br><span class="line">			...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//关注点：执行目标方法</span></span><br><span class="line">			invocableMethod.invokeAndHandle(webRequest, mavContainer);</span><br><span class="line">			<span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">				<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			webRequest.requestCompleted();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p><code>invokeAndHandle()</code>方法如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">invokeAndHandle</span><span class="params">(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">Object</span> <span class="variable">returnValue</span> <span class="operator">=</span> invokeForRequest(webRequest, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//returnValue存储起来</span></span><br><span class="line">			<span class="built_in">this</span>.returnValueHandlers.handleReturnValue(</span><br><span class="line">					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">			...</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//InvocableHandlerMethod类的，ServletInvocableHandlerMethod类继承InvocableHandlerMethod类</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">invokeForRequest</span><span class="params">(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">////获取方法的参数值</span></span><br><span class="line">		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">       </span><br><span class="line">		<span class="keyword">return</span> doInvoke(args);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">protected</span> Object <span class="title function_">doInvoke</span><span class="params">(Object... args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		<span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> getBridgedMethod();<span class="comment">//@RequestMapping的方法</span></span><br><span class="line">		ReflectionUtils.makeAccessible(method);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (KotlinDetector.isSuspendingFunction(method)) &#123;</span><br><span class="line">				<span class="keyword">return</span> CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);</span><br><span class="line">			&#125;</span><br><span class="line">            <span class="comment">//通过反射调用</span></span><br><span class="line">			<span class="keyword">return</span> method.invoke(getBean(), args);<span class="comment">//getBean()指@RequestMapping的方法所在类的对象。</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (IllegalArgumentException ex) &#123;</span><br><span class="line">			...</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (InvocationTargetException ex) &#123;</span><br><span class="line">			...</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">&#125;   </span><br></pre></td></tr></table></figure>



<h3 id="如何确定目标方法每一个参数的值"><a href="#如何确定目标方法每一个参数的值" class="headerlink" title="如何确定目标方法每一个参数的值"></a>如何确定目标方法每一个参数的值</h3><p>重点分析<code>ServletInvocableHandlerMethod</code>的<code>getMethodArgumentValues</code>方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span><span class="comment">//InvocableHandlerMethod类的，ServletInvocableHandlerMethod类继承InvocableHandlerMethod类</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">invokeForRequest</span><span class="params">(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">////获取方法的参数值</span></span><br><span class="line">		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">       </span><br><span class="line">		<span class="keyword">return</span> doInvoke(args);</span><br><span class="line">	&#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">//本节重点，获取方法的参数值</span></span><br><span class="line">	<span class="keyword">protected</span> Object[] getMethodArgumentValues(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span><br><span class="line">			Object... providedArgs) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		MethodParameter[] parameters = getMethodParameters();</span><br><span class="line">		<span class="keyword">if</span> (ObjectUtils.isEmpty(parameters)) &#123;</span><br><span class="line">			<span class="keyword">return</span> EMPTY_ARGS;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		Object[] args = <span class="keyword">new</span> <span class="title class_">Object</span>[parameters.length];</span><br><span class="line">		<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; parameters.length; i++) &#123;</span><br><span class="line">			<span class="type">MethodParameter</span> <span class="variable">parameter</span> <span class="operator">=</span> parameters[i];</span><br><span class="line">			parameter.initParameterNameDiscovery(<span class="built_in">this</span>.parameterNameDiscoverer);</span><br><span class="line">			args[i] = findProvidedArgument(parameter, providedArgs);</span><br><span class="line">			<span class="keyword">if</span> (args[i] != <span class="literal">null</span>) &#123;</span><br><span class="line">				<span class="keyword">continue</span>;</span><br><span class="line">			&#125;</span><br><span class="line">            <span class="comment">//查看resolvers是否有支持</span></span><br><span class="line">			<span class="keyword">if</span> (!<span class="built_in">this</span>.resolvers.supportsParameter(parameter)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(formatArgumentError(parameter, <span class="string">&quot;No suitable resolver&quot;</span>));</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//支持的话就开始解析吧</span></span><br><span class="line">				args[i] = <span class="built_in">this</span>.resolvers.resolveArgument(parameter, mavContainer, request, <span class="built_in">this</span>.dataBinderFactory);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">				....</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> args;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>this.resolvers</code>的类型为<code>HandlerMethodArgumentResolverComposite</code>（在<a href="#">参数解析器</a>章节提及）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line">    </span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> getArgumentResolver(parameter) != <span class="literal">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">HandlerMethodArgumentResolver</span> <span class="variable">resolver</span> <span class="operator">=</span> getArgumentResolver(parameter);</span><br><span class="line">		<span class="keyword">if</span> (resolver == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Unsupported parameter type [&quot;</span> +</span><br><span class="line">					parameter.getParameterType().getName() + <span class="string">&quot;]. supportsParameter should be called first.&quot;</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> HandlerMethodArgumentResolver <span class="title function_">getArgumentResolver</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		<span class="type">HandlerMethodArgumentResolver</span> <span class="variable">result</span> <span class="operator">=</span> <span class="built_in">this</span>.argumentResolverCache.get(parameter);</span><br><span class="line">		<span class="keyword">if</span> (result == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">//挨个判断所有参数解析器那个支持解析这个参数</span></span><br><span class="line">			<span class="keyword">for</span> (HandlerMethodArgumentResolver resolver : <span class="built_in">this</span>.argumentResolvers) &#123;</span><br><span class="line">				<span class="keyword">if</span> (resolver.supportsParameter(parameter)) &#123;</span><br><span class="line">					result = resolver;</span><br><span class="line">					<span class="built_in">this</span>.argumentResolverCache.put(parameter, result);<span class="comment">//找到了，resolver就缓存起来，方便稍后resolveArgument()方法使用</span></span><br><span class="line">					<span class="keyword">break</span>;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> result;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>本节描述，一个请求发送到 DispatcherServlet 后的具体处理流程，也就是 SpringMVC 的主要原理</p>
<p>本节内容较多且硬核，对日后编程很有帮助，需耐心对待。</p>
<p>可以运行一个示例，打断点，在 Debug 模式下，查看程序流程</p>
<h2 id="33、请求处理-【源码分析】-Servlet-API-参数解析原理"><a href="#33、请求处理-【源码分析】-Servlet-API-参数解析原理" class="headerlink" title="33、请求处理-【源码分析】-Servlet API 参数解析原理"></a>33、请求处理-【源码分析】-Servlet API 参数解析原理</h2><ul>
<li>WebRequest</li>
<li>ServletRequest</li>
<li>MultipartRequest</li>
<li>HttpSession</li>
<li>javax.servlet.http.PushBuilder</li>
<li>Principal</li>
<li>InputStream</li>
<li>Reader</li>
<li>HttpMethod</li>
<li>Locale</li>
<li>TimeZone</li>
<li>ZoneId</li>
</ul>
<p><strong>ServletRequestMethodArgumentResolver</strong>用来处理以上的参数</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletRequestMethodArgumentResolver</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> Class&lt;?&gt; pushBuilder;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">static</span> &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			pushBuilder = ClassUtils.forName(<span class="string">&quot;javax.servlet.http.PushBuilder&quot;</span>,</span><br><span class="line">					ServletRequestMethodArgumentResolver.class.getClassLoader());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (ClassNotFoundException ex) &#123;</span><br><span class="line">			<span class="comment">// Servlet 4.0 PushBuilder not found - not supported for injection</span></span><br><span class="line">			pushBuilder = <span class="literal">null</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		Class&lt;?&gt; paramType = parameter.getParameterType();</span><br><span class="line">		<span class="keyword">return</span> (WebRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">				ServletRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">				MultipartRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">				HttpSession.class.isAssignableFrom(paramType) ||</span><br><span class="line">				(pushBuilder != <span class="literal">null</span> &amp;&amp; pushBuilder.isAssignableFrom(paramType)) ||</span><br><span class="line">				(Principal.class.isAssignableFrom(paramType) &amp;&amp; !parameter.hasParameterAnnotations()) ||</span><br><span class="line">				InputStream.class.isAssignableFrom(paramType) ||</span><br><span class="line">				Reader.class.isAssignableFrom(paramType) ||</span><br><span class="line">				HttpMethod.class == paramType ||</span><br><span class="line">				Locale.class == paramType ||</span><br><span class="line">				TimeZone.class == paramType ||</span><br><span class="line">				ZoneId.class == paramType);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		Class&lt;?&gt; paramType = parameter.getParameterType();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// WebRequest / NativeWebRequest / ServletWebRequest</span></span><br><span class="line">		<span class="keyword">if</span> (WebRequest.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="keyword">if</span> (!paramType.isInstance(webRequest)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Current request is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + webRequest);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> webRequest;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest</span></span><br><span class="line">		<span class="keyword">if</span> (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="keyword">return</span> resolveNativeRequest(webRequest, paramType);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// HttpServletRequest required for all further argument types</span></span><br><span class="line">		<span class="keyword">return</span> resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> &lt;T&gt; T <span class="title function_">resolveNativeRequest</span><span class="params">(NativeWebRequest webRequest, Class&lt;T&gt; requiredType)</span> &#123;</span><br><span class="line">		<span class="type">T</span> <span class="variable">nativeRequest</span> <span class="operator">=</span> webRequest.getNativeRequest(requiredType);</span><br><span class="line">		<span class="keyword">if</span> (nativeRequest == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">					<span class="string">&quot;Current request is not of type [&quot;</span> + requiredType.getName() + <span class="string">&quot;]: &quot;</span> + webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> nativeRequest;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> Object <span class="title function_">resolveArgument</span><span class="params">(Class&lt;?&gt; paramType, HttpServletRequest request)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">		<span class="keyword">if</span> (HttpSession.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="type">HttpSession</span> <span class="variable">session</span> <span class="operator">=</span> request.getSession();</span><br><span class="line">			<span class="keyword">if</span> (session != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(session)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Current session is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + session);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> session;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (pushBuilder != <span class="literal">null</span> &amp;&amp; pushBuilder.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="keyword">return</span> PushBuilderDelegate.resolvePushBuilder(request, paramType);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (InputStream.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> request.getInputStream();</span><br><span class="line">			<span class="keyword">if</span> (inputStream != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(inputStream)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Request input stream is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + inputStream);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> inputStream;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (Reader.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="type">Reader</span> <span class="variable">reader</span> <span class="operator">=</span> request.getReader();</span><br><span class="line">			<span class="keyword">if</span> (reader != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(reader)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Request body reader is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + reader);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> reader;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (Principal.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">			<span class="type">Principal</span> <span class="variable">userPrincipal</span> <span class="operator">=</span> request.getUserPrincipal();</span><br><span class="line">			<span class="keyword">if</span> (userPrincipal != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(userPrincipal)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Current user principal is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + userPrincipal);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> userPrincipal;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (HttpMethod.class == paramType) &#123;</span><br><span class="line">			<span class="keyword">return</span> HttpMethod.resolve(request.getMethod());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (Locale.class == paramType) &#123;</span><br><span class="line">			<span class="keyword">return</span> RequestContextUtils.getLocale(request);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (TimeZone.class == paramType) &#123;</span><br><span class="line">			<span class="type">TimeZone</span> <span class="variable">timeZone</span> <span class="operator">=</span> RequestContextUtils.getTimeZone(request);</span><br><span class="line">			<span class="keyword">return</span> (timeZone != <span class="literal">null</span> ? timeZone : TimeZone.getDefault());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (ZoneId.class == paramType) &#123;</span><br><span class="line">			<span class="type">TimeZone</span> <span class="variable">timeZone</span> <span class="operator">=</span> RequestContextUtils.getTimeZone(request);</span><br><span class="line">			<span class="keyword">return</span> (timeZone != <span class="literal">null</span> ? timeZone.toZoneId() : ZoneId.systemDefault());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Should never happen...</span></span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Unknown parameter type: &quot;</span> + paramType.getName());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PushBuilderDelegate</span> &#123;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Nullable</span></span><br><span class="line">		<span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">resolvePushBuilder</span><span class="params">(HttpServletRequest request, Class&lt;?&gt; paramType)</span> &#123;</span><br><span class="line">			<span class="type">PushBuilder</span> <span class="variable">pushBuilder</span> <span class="operator">=</span> request.newPushBuilder();</span><br><span class="line">			<span class="keyword">if</span> (pushBuilder != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(pushBuilder)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">						<span class="string">&quot;Current push builder is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + pushBuilder);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> pushBuilder;</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>用例：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/goto&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">goToPage</span><span class="params">(HttpServletRequest request)</span>&#123;</span><br><span class="line"></span><br><span class="line">        request.setAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;成功了...&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;code&quot;</span>,<span class="number">200</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;  <span class="comment">//转发到  /success请求</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="34、请求处理-【源码分析】-Model、Map原理"><a href="#34、请求处理-【源码分析】-Model、Map原理" class="headerlink" title="34、请求处理-【源码分析】-Model、Map原理"></a>34、请求处理-【源码分析】-Model、Map原理</h2><p>复杂参数：</p>
<ul>
<li><p><strong>Map</strong></p>
</li>
<li><p><strong>Model（map、model里面的数据会被放在request的请求域  request.setAttribute）</strong></p>
</li>
<li><p>Errors&#x2F;BindingResult</p>
</li>
<li><p><strong>RedirectAttributes（ 重定向携带数据）</strong></p>
</li>
<li><p><strong>ServletResponse（response）</strong></p>
</li>
<li><p>SessionStatus</p>
</li>
<li><p>UriComponentsBuilder</p>
</li>
<li><p>ServletUriComponentsBuilder</p>
</li>
</ul>
<p>用例：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/params&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">testParam</span><span class="params">(Map&lt;String,Object&gt; map,</span></span><br><span class="line"><span class="params">                        Model model,</span></span><br><span class="line"><span class="params">                        HttpServletRequest request,</span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span>&#123;</span><br><span class="line">    <span class="comment">//下面三位都是可以给request域中放数据</span></span><br><span class="line">    map.put(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world666&quot;</span>);</span><br><span class="line">    model.addAttribute(<span class="string">&quot;world&quot;</span>,<span class="string">&quot;hello666&quot;</span>);</span><br><span class="line">    request.setAttribute(<span class="string">&quot;message&quot;</span>,<span class="string">&quot;HelloWorld&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">Cookie</span> <span class="variable">cookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;c1&quot;</span>,<span class="string">&quot;v1&quot;</span>);</span><br><span class="line">    response.addCookie(cookie);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/success&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Map <span class="title function_">success</span><span class="params">(<span class="meta">@RequestAttribute(value = &quot;msg&quot;,required = false)</span> String msg,</span></span><br><span class="line"><span class="params">                   <span class="meta">@RequestAttribute(value = &quot;code&quot;,required = false)</span>Integer code,</span></span><br><span class="line"><span class="params">                   HttpServletRequest request)</span>&#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">msg1</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;msg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="type">Object</span> <span class="variable">hello</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;hello&quot;</span>);<span class="comment">//得出testParam方法赋予的值 world666</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">world</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;world&quot;</span>);<span class="comment">//得出testParam方法赋予的值 hello666</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">message</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;message&quot;</span>);<span class="comment">//得出testParam方法赋予的值 HelloWorld</span></span><br><span class="line"></span><br><span class="line">    map.put(<span class="string">&quot;reqMethod_msg&quot;</span>,msg1);</span><br><span class="line">    map.put(<span class="string">&quot;annotation_msg&quot;</span>,msg);</span><br><span class="line">    map.put(<span class="string">&quot;hello&quot;</span>,hello);</span><br><span class="line">    map.put(<span class="string">&quot;world&quot;</span>,world);</span><br><span class="line">    map.put(<span class="string">&quot;message&quot;</span>,message);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> map;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>Map&lt;String,Object&gt; map</code></p>
</li>
<li><p><code>Model model</code></p>
</li>
<li><p><code>HttpServletRequest request</code></p>
</li>
</ul>
<p>上面三位都是可以给request域中放数据，用<code>request.getAttribute()</code>获取</p>
<p>接下来我们看看，<code>Map&lt;String,Object&gt; map</code>与<code>Model model</code>用什么参数处理器。</p>
<hr>
<p><code>Map&lt;String,Object&gt; map</code>参数用<code>MapMethodProcessor</code>处理：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MapMethodProcessor</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span>, HandlerMethodReturnValueHandler &#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> (Map.class.isAssignableFrom(parameter.getParameterType()) &amp;&amp;</span><br><span class="line">				parameter.getParameterAnnotations().length == <span class="number">0</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		Assert.state(mavContainer != <span class="literal">null</span>, <span class="string">&quot;ModelAndViewContainer is required for model exposure&quot;</span>);</span><br><span class="line">		<span class="keyword">return</span> mavContainer.getModel();</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>mavContainer.getModel()</code>如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ModelAndViewContainer</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ModelMap</span> <span class="variable">defaultModel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BindingAwareModelMap</span>();</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> ModelMap redirectModel;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> ModelMap <span class="title function_">getModel</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">if</span> (useDefaultModel()) &#123;</span><br><span class="line">			<span class="keyword">return</span> <span class="built_in">this</span>.defaultModel;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.redirectModel == <span class="literal">null</span>) &#123;</span><br><span class="line">				<span class="built_in">this</span>.redirectModel = <span class="keyword">new</span> <span class="title class_">ModelMap</span>();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> <span class="built_in">this</span>.redirectModel;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">useDefaultModel</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> (!<span class="built_in">this</span>.redirectModelScenario || (<span class="built_in">this</span>.redirectModel == <span class="literal">null</span> &amp;&amp; !<span class="built_in">this</span>.ignoreDefaultModelOnRedirect));</span><br><span class="line">	&#125;</span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<p><code>Model model</code>用<code>ModelMethodProcessor</code>处理：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ModelMethodProcessor</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span>, HandlerMethodReturnValueHandler &#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> Model.class.isAssignableFrom(parameter.getParameterType());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		Assert.state(mavContainer != <span class="literal">null</span>, <span class="string">&quot;ModelAndViewContainer is required for model exposure&quot;</span>);</span><br><span class="line">		<span class="keyword">return</span> mavContainer.getModel();</span><br><span class="line">	&#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>return mavContainer.getModel();</code>这跟<code>MapMethodProcessor</code>的一致</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205010247689.png" alt="在这里插入图片描述"></p>
<p><code>Model</code>也是另一种意义的<code>Map</code>。</p>
<hr>
<p><strong>接下来看看</strong><code>Map&lt;String,Object&gt; map</code>与<code>Model model</code>值是如何做到用<code>request.getAttribute()</code>获取的。</p>
<p>众所周知，所有的数据都放在 <strong>ModelAndView</strong>包含要去的页面地址View，还包含Model数据。</p>
<p>先看<strong>ModelAndView</strong>接下来是如何处理的？</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		...</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            </span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Actually invoke the handler.</span></span><br><span class="line">			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br><span class="line">            </span><br><span class="line">            ...</span><br><span class="line">            </span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">				dispatchException = ex;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Throwable err) &#123;</span><br><span class="line">				<span class="comment">// As of 4.3, we&#x27;re processing Errors thrown from handler methods as well,</span></span><br><span class="line">				<span class="comment">// making them available for @ExceptionHandler methods and other scenarios.</span></span><br><span class="line">				dispatchException = <span class="keyword">new</span> <span class="title class_">NestedServletException</span>(<span class="string">&quot;Handler dispatch failed&quot;</span>, err);</span><br><span class="line">			&#125;</span><br><span class="line">        	<span class="comment">//处理分发结果</span></span><br><span class="line">			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);</span><br><span class="line">		&#125;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">processDispatchResult</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span><br><span class="line"><span class="params">			<span class="meta">@Nullable</span> HandlerExecutionChain mappedHandler, <span class="meta">@Nullable</span> ModelAndView mv,</span></span><br><span class="line"><span class="params">			<span class="meta">@Nullable</span> Exception exception)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Did the handler return a view to render?</span></span><br><span class="line">		<span class="keyword">if</span> (mv != <span class="literal">null</span> &amp;&amp; !mv.wasCleared()) &#123;</span><br><span class="line">			render(mv, request, response);</span><br><span class="line">			...</span><br><span class="line">		&#125;</span><br><span class="line">		...</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">render</span><span class="params">(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		...</span><br><span class="line"></span><br><span class="line">		View view;</span><br><span class="line">		<span class="type">String</span> <span class="variable">viewName</span> <span class="operator">=</span> mv.getViewName();</span><br><span class="line">		<span class="keyword">if</span> (viewName != <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="comment">// We need to resolve the view name.</span></span><br><span class="line">			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);</span><br><span class="line">			<span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(<span class="string">&quot;Could not resolve view with name &#x27;&quot;</span> + mv.getViewName() +</span><br><span class="line">						<span class="string">&quot;&#x27; in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="comment">// No need to lookup: the ModelAndView object contains the actual View object.</span></span><br><span class="line">			view = mv.getView();</span><br><span class="line">			<span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(<span class="string">&quot;ModelAndView [&quot;</span> + mv + <span class="string">&quot;] neither contains a view name nor a &quot;</span> +</span><br><span class="line">						<span class="string">&quot;View object in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		view.render(mv.getModelInternal(), request, response);</span><br><span class="line">        </span><br><span class="line">        ...</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在Debug模式下，<code>view</code>属为<code>InternalResourceView</code>类。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InternalResourceView</span> <span class="keyword">extends</span> <span class="title class_">AbstractUrlBasedView</span> &#123;</span><br><span class="line">    </span><br><span class="line"> 	<span class="meta">@Override</span><span class="comment">//该方法在AbstractView，AbstractUrlBasedView继承了AbstractView</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">render</span><span class="params">(<span class="meta">@Nullable</span> Map&lt;String, ?&gt; model, HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		</span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">		Map&lt;String, Object&gt; mergedModel = createMergedOutputModel(model, request, response);</span><br><span class="line">		prepareResponse(request, response);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//看下一个方法实现</span></span><br><span class="line">		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">renderMergedOutputModel</span><span class="params">(</span></span><br><span class="line"><span class="params">			Map&lt;String, Object&gt; model, HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Expose the model object as request attributes.</span></span><br><span class="line">        <span class="comment">// 暴露模型作为请求域属性</span></span><br><span class="line">		exposeModelAsRequestAttributes(model, request);<span class="comment">//&lt;---重点</span></span><br><span class="line"></span><br><span class="line">		<span class="comment">// Expose helpers as request attributes, if any.</span></span><br><span class="line">		exposeHelpers(request);</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Determine the path for the request dispatcher.</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">dispatcherPath</span> <span class="operator">=</span> prepareForRendering(request, response);</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Obtain a RequestDispatcher for the target resource (typically a JSP).</span></span><br><span class="line">		<span class="type">RequestDispatcher</span> <span class="variable">rd</span> <span class="operator">=</span> getRequestDispatcher(request, dispatcherPath);</span><br><span class="line">		</span><br><span class="line">        ...</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//该方法在AbstractView，AbstractUrlBasedView继承了AbstractView</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">exposeModelAsRequestAttributes</span><span class="params">(Map&lt;String, Object&gt; model,</span></span><br><span class="line"><span class="params">			HttpServletRequest request)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		model.forEach((name, value) -&gt; &#123;</span><br><span class="line">			<span class="keyword">if</span> (value != <span class="literal">null</span>) &#123;</span><br><span class="line">				request.setAttribute(name, value);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">else</span> &#123;</span><br><span class="line">				request.removeAttribute(name);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>exposeModelAsRequestAttributes</code>方法看出，<code>Map&lt;String,Object&gt; map</code>，<code>Model model</code>这两种类型数据可以给request域中放数据，用<code>request.getAttribute()</code>获取。</p>
<h2 id="35、请求处理-【源码分析】-自定义参数绑定原理"><a href="#35、请求处理-【源码分析】-自定义参数绑定原理" class="headerlink" title="35、请求处理-【源码分析】-自定义参数绑定原理"></a>35、请求处理-【源码分析】-自定义参数绑定原理</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 数据绑定：页面提交的请求数据（GET、POST）都可以和对象属性进行绑定</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> person</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/saveuser&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">saveuser</span><span class="params">(Person person)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *     姓名： &lt;input name=&quot;userName&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     年龄： &lt;input name=&quot;age&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     生日： &lt;input name=&quot;birth&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     宠物姓名：&lt;input name=&quot;pet.name&quot;/&gt;&lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     宠物年龄：&lt;input name=&quot;pet.age&quot;/&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Date birth;</span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pet</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>封装过程用到<code>ServletModelAttributeMethodProcessor</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletModelAttributeMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">ModelAttributeMethodProcessor</span> &#123;</span><br><span class="line">	</span><br><span class="line">    <span class="meta">@Override</span><span class="comment">//本方法在ModelAttributeMethodProcessor类，</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> (parameter.hasParameterAnnotation(ModelAttribute.class) ||</span><br><span class="line">				(<span class="built_in">this</span>.annotationNotRequired &amp;&amp; !BeanUtils.isSimpleProperty(parameter.getParameterType())));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span><span class="comment">//本方法在ModelAttributeMethodProcessor类，</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		...</span><br><span class="line"></span><br><span class="line">		<span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> ModelFactory.getNameForParameter(parameter);</span><br><span class="line">		<span class="type">ModelAttribute</span> <span class="variable">ann</span> <span class="operator">=</span> parameter.getParameterAnnotation(ModelAttribute.class);</span><br><span class="line">		<span class="keyword">if</span> (ann != <span class="literal">null</span>) &#123;</span><br><span class="line">			mavContainer.setBinding(name, ann.binding());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="type">Object</span> <span class="variable">attribute</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">		<span class="type">BindingResult</span> <span class="variable">bindingResult</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (mavContainer.containsAttribute(name)) &#123;</span><br><span class="line">			attribute = mavContainer.getModel().get(name);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="comment">// Create attribute instance</span></span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				attribute = createAttribute(name, parameter, binderFactory, webRequest);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (BindException ex) &#123;</span><br><span class="line">				...</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (bindingResult == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="comment">// Bean property binding and validation;</span></span><br><span class="line">			<span class="comment">// skipped in case of binding failure on construction.</span></span><br><span class="line">			<span class="type">WebDataBinder</span> <span class="variable">binder</span> <span class="operator">=</span> binderFactory.createBinder(webRequest, attribute, name);</span><br><span class="line">			<span class="keyword">if</span> (binder.getTarget() != <span class="literal">null</span>) &#123;</span><br><span class="line">				<span class="keyword">if</span> (!mavContainer.isBindingDisabled(name)) &#123;</span><br><span class="line">                    <span class="comment">//web数据绑定器，将请求参数的值绑定到指定的JavaBean里面**</span></span><br><span class="line">					bindRequestParameters(binder, webRequest);</span><br><span class="line">				&#125;</span><br><span class="line">				validateIfApplicable(binder, parameter);</span><br><span class="line">				<span class="keyword">if</span> (binder.getBindingResult().hasErrors() &amp;&amp; isBindExceptionRequired(binder, parameter)) &#123;</span><br><span class="line">					<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">BindException</span>(binder.getBindingResult());</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="comment">// Value type adaptation, also covering java.util.Optional</span></span><br><span class="line">			<span class="keyword">if</span> (!parameter.getParameterType().isInstance(attribute)) &#123;</span><br><span class="line">				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);</span><br><span class="line">			&#125;</span><br><span class="line">			bindingResult = binder.getBindingResult();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Add resolved attribute and BindingResult at the end of the model</span></span><br><span class="line">		Map&lt;String, Object&gt; bindingResultModel = bindingResult.getModel();</span><br><span class="line">		mavContainer.removeAttributes(bindingResultModel);</span><br><span class="line">		mavContainer.addAllAttributes(bindingResultModel);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> attribute;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中</strong></p>
<p><strong>在过程当中，用到GenericConversionService：在设置每一个值的时候，找它里面的所有converter那个可以将这个数据类型（request带来参数的字符串）转换到指定的类型</strong></p>
<h2 id="36、请求处理-【源码分析】-自定义Converter原理"><a href="#36、请求处理-【源码分析】-自定义Converter原理" class="headerlink" title="36、请求处理-【源码分析】-自定义Converter原理"></a>36、请求处理-【源码分析】-自定义Converter原理</h2><p>未来我们可以给WebDataBinder里面放自己的Converter；</p>
<p>下面演示将字符串<code>“啊猫,3”</code>转换成<code>Pet</code>对象。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//1、WebMvcConfigurer定制化SpringMVC的功能</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFormatters</span><span class="params">(FormatterRegistry registry)</span> &#123;</span><br><span class="line">            registry.addConverter(<span class="keyword">new</span> <span class="title class_">Converter</span>&lt;String, Pet&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> Pet <span class="title function_">convert</span><span class="params">(String source)</span> &#123;</span><br><span class="line">                    <span class="comment">// 啊猫,3</span></span><br><span class="line">                    <span class="keyword">if</span>(!StringUtils.isEmpty(source))&#123;</span><br><span class="line">                        <span class="type">Pet</span> <span class="variable">pet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Pet</span>();</span><br><span class="line">                        String[] split = source.split(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">                        pet.setName(split[<span class="number">0</span>]);</span><br><span class="line">                        pet.setAge(Integer.parseInt(split[<span class="number">1</span>]));</span><br><span class="line">                        <span class="keyword">return</span> pet;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="37、响应处理-【源码分析】-ReturnValueHandler原理"><a href="#37、响应处理-【源码分析】-ReturnValueHandler原理" class="headerlink" title="37、响应处理-【源码分析】-ReturnValueHandler原理"></a>37、响应处理-【源码分析】-ReturnValueHandler原理</h2><p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205010403920.jpg" alt="在这里插入图片描述"></p>
<p>假设给前端自动返回json数据，需要引入相关的依赖</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- web场景自动引入了json场景 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-json<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>控制层代码如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponseTestController</span> &#123;</span><br><span class="line">    </span><br><span class="line">	<span class="meta">@ResponseBody</span>  <span class="comment">//利用返回值处理器里面的消息转换器进行处理</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &quot;/test/person&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">getPerson</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        person.setAge(<span class="number">28</span>);</span><br><span class="line">        person.setBirth(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        person.setUserName(<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><a href="#">32、请求处理-【源码分析】-各种类型参数解析原理 - 返回值处理器</a>有讨论<strong>ReturnValueHandler</strong>。现在直接看看重点：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			</span><br><span class="line">            ...</span><br><span class="line">            </span><br><span class="line">            <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">                </span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">				invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;----关注点</span></span><br><span class="line">				invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">			invocableMethod.invokeAndHandle(webRequest, mavContainer);<span class="comment">//看下块代码</span></span><br><span class="line">			<span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">				<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			webRequest.requestCompleted();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">invokeAndHandle</span><span class="params">(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">			Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">Object</span> <span class="variable">returnValue</span> <span class="operator">=</span> invokeForRequest(webRequest, mavContainer, providedArgs);</span><br><span class="line">		</span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//看下块代码</span></span><br><span class="line">			<span class="built_in">this</span>.returnValueHandlers.handleReturnValue(</span><br><span class="line">					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">			...</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//selectHandler()实现在下面</span></span><br><span class="line">		<span class="type">HandlerMethodReturnValueHandler</span> <span class="variable">handler</span> <span class="operator">=</span> selectHandler(returnValue, returnType);</span><br><span class="line">		<span class="keyword">if</span> (handler == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Unknown return value type: &quot;</span> + returnType.getParameterType().getName());</span><br><span class="line">		&#125;</span><br><span class="line">        <span class="comment">//开始处理</span></span><br><span class="line">		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">   	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> HandlerMethodReturnValueHandler <span class="title function_">selectHandler</span><span class="params">(<span class="meta">@Nullable</span> Object value, MethodParameter returnType)</span> &#123;</span><br><span class="line">		<span class="type">boolean</span> <span class="variable">isAsyncValue</span> <span class="operator">=</span> isAsyncReturnValue(value, returnType);</span><br><span class="line">		<span class="keyword">for</span> (HandlerMethodReturnValueHandler handler : <span class="built_in">this</span>.returnValueHandlers) &#123;</span><br><span class="line">			<span class="keyword">if</span> (isAsyncValue &amp;&amp; !(handler <span class="keyword">instanceof</span> AsyncHandlerMethodReturnValueHandler)) &#123;</span><br><span class="line">				<span class="keyword">continue</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (handler.supportsReturnType(returnType)) &#123;</span><br><span class="line">				<span class="keyword">return</span> handler;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br></pre></td></tr></table></figure>



<p><code>@ResponseBody</code> 注解，即<code>RequestResponseBodyMethodProcessor</code>，它实现<code>HandlerMethodReturnValueHandler</code>接口</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestResponseBodyMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">   </span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span></span><br><span class="line">			<span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">		mavContainer.setRequestHandled(<span class="literal">true</span>);</span><br><span class="line">		<span class="type">ServletServerHttpRequest</span> <span class="variable">inputMessage</span> <span class="operator">=</span> createInputMessage(webRequest);</span><br><span class="line">		<span class="type">ServletServerHttpResponse</span> <span class="variable">outputMessage</span> <span class="operator">=</span> createOutputMessage(webRequest);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用消息转换器进行写出操作，本方法下一章节介绍：</span></span><br><span class="line">		<span class="comment">// Try even with null return value. ResponseBodyAdvice could get involved.</span></span><br><span class="line">		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="38、响应处理-【源码分析】-HTTPMessageConverter原理"><a href="#38、响应处理-【源码分析】-HTTPMessageConverter原理" class="headerlink" title="38、响应处理-【源码分析】-HTTPMessageConverter原理"></a>38、响应处理-【源码分析】-HTTPMessageConverter原理</h2><p>返回值处理器<code>ReturnValueHandler</code>原理：</p>
<ol>
<li>返回值处理器判断是否支持这种类型返回值 <code>supportsReturnType</code></li>
<li>返回值处理器调用 <code>handleReturnValue</code> 进行处理</li>
<li><code>RequestResponseBodyMethodProcessor</code> 可以处理返回值标了<code>@ResponseBody</code> 注解的。<ul>
<li>利用 <code>MessageConverters</code> 进行处理 将数据写为json<ol>
<li>内容协商（浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型）</li>
<li>服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据，</li>
<li>SpringMVC会挨个遍历所有容器底层的 <code>HttpMessageConverter</code> ，看谁能处理？<ol>
<li>得到<code>MappingJackson2HttpMessageConverter</code>可以将对象写为json</li>
<li>利用<code>MappingJackson2HttpMessageConverter</code>将对象转为json再写出去。</li>
</ol>
</li>
</ol>
</li>
</ul>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//承接上一节内容</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (value <span class="keyword">instanceof</span> CharSequence) &#123;</span><br><span class="line">                body = value.toString();</span><br><span class="line">                valueType = String.class;</span><br><span class="line">                targetType = String.class;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                body = value;</span><br><span class="line">                valueType = getReturnValueType(body, returnType);</span><br><span class="line">                targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">			...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (body != <span class="literal">null</span> &amp;&amp; producibleTypes.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMessageNotWritableException</span>(</span><br><span class="line">                            <span class="string">&quot;No converter found for return value of type: &quot;</span> + valueType);</span><br><span class="line">                &#125;</span><br><span class="line">                List&lt;MediaType&gt; mediaTypesToUse = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">                <span class="keyword">for</span> (MediaType requestedType : acceptableTypes) &#123;</span><br><span class="line">                    <span class="keyword">for</span> (MediaType producibleType : producibleTypes) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (requestedType.isCompatibleWith(producibleType)) &#123;</span><br><span class="line">                            mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (mediaTypesToUse.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                        logger.debug(<span class="string">&quot;No match for &quot;</span> + acceptableTypes + <span class="string">&quot;, supported: &quot;</span> + producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                MediaType.sortBySpecificityAndQuality(mediaTypesToUse);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//选择一个MediaType</span></span><br><span class="line">                <span class="keyword">for</span> (MediaType mediaType : mediaTypesToUse) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mediaType.isConcrete()) &#123;</span><br><span class="line">                        selectedMediaType = mediaType;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) &#123;</span><br><span class="line">                        selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Using &#x27;&quot;</span> + selectedMediaType + <span class="string">&quot;&#x27;, given &quot;</span> +</span><br><span class="line">                            acceptableTypes + <span class="string">&quot; and supported &quot;</span> + producibleTypes);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        	</span><br><span class="line">            <span class="keyword">if</span> (selectedMediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                selectedMediaType = selectedMediaType.removeQualityValue();</span><br><span class="line">                <span class="comment">//本节主角：HttpMessageConverter</span></span><br><span class="line">                <span class="keyword">for</span> (HttpMessageConverter&lt;?&gt; converter : <span class="built_in">this</span>.messageConverters) &#123;</span><br><span class="line">                    <span class="type">GenericHttpMessageConverter</span> <span class="variable">genericConverter</span> <span class="operator">=</span> (converter <span class="keyword">instanceof</span> GenericHttpMessageConverter ?</span><br><span class="line">                            (GenericHttpMessageConverter&lt;?&gt;) converter : <span class="literal">null</span>);</span><br><span class="line">                    </span><br><span class="line">                    <span class="comment">//判断是否可写</span></span><br><span class="line">                    <span class="keyword">if</span> (genericConverter != <span class="literal">null</span> ?</span><br><span class="line">                            ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :</span><br><span class="line">                            converter.canWrite(valueType, selectedMediaType)) &#123;</span><br><span class="line">                        body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,</span><br><span class="line">                                (Class&lt;? <span class="keyword">extends</span> <span class="title class_">HttpMessageConverter</span>&lt;?&gt;&gt;) converter.getClass(),</span><br><span class="line">                                inputMessage, outputMessage);</span><br><span class="line">                        <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                            <span class="type">Object</span> <span class="variable">theBody</span> <span class="operator">=</span> body;</span><br><span class="line">                            LogFormatUtils.traceDebug(logger, traceOn -&gt;</span><br><span class="line">                                    <span class="string">&quot;Writing [&quot;</span> + LogFormatUtils.formatValue(theBody, !traceOn) + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">                            addContentDispositionHeader(inputMessage, outputMessage);</span><br><span class="line">							<span class="comment">//开始写入</span></span><br><span class="line">                            <span class="keyword">if</span> (genericConverter != <span class="literal">null</span>) &#123;</span><br><span class="line">                                genericConverter.write(body, targetType, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">else</span> &#123;</span><br><span class="line">                                ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                                logger.debug(<span class="string">&quot;Nothing to write: null body&quot;</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">			...</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<p><code>HTTPMessageConverter</code>接口：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Strategy interface for converting from and to HTTP requests and responses.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HttpMessageConverter</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Indicates whether the given class can be read by this converter.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="type">boolean</span> <span class="title function_">canRead</span><span class="params">(Class&lt;?&gt; clazz, <span class="meta">@Nullable</span> MediaType mediaType)</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Indicates whether the given class can be written by this converter.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="type">boolean</span> <span class="title function_">canWrite</span><span class="params">(Class&lt;?&gt; clazz, <span class="meta">@Nullable</span> MediaType mediaType)</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Return the list of &#123;<span class="doctag">@link</span> MediaType&#125; objects supported by this converter.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	List&lt;MediaType&gt; <span class="title function_">getSupportedMediaTypes</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Read an object of the given type from the given input message, and returns it.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	T <span class="title function_">read</span><span class="params">(Class&lt;? extends T&gt; clazz, HttpInputMessage inputMessage)</span></span><br><span class="line">			<span class="keyword">throws</span> IOException, HttpMessageNotReadableException;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Write an given object to the given output message.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">void</span> <span class="title function_">write</span><span class="params">(T t, <span class="meta">@Nullable</span> MediaType contentType, HttpOutputMessage outputMessage)</span></span><br><span class="line">			<span class="keyword">throws</span> IOException, HttpMessageNotWritableException;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><code>HttpMessageConverter</code>: 看是否支持将 此 <code>Class</code>类型的对象，转为<code>MediaType</code>类型的数据。</p>
<p>例子：<code>Person</code>对象转为JSON，或者 JSON转为<code>Person</code>，这将用到<code>MappingJackson2HttpMessageConverter</code></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/20210205010509984.png" alt="在这里插入图片描述"></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MappingJackson2HttpMessageConverter</span> <span class="keyword">extends</span> <span class="title class_">AbstractJackson2HttpMessageConverter</span> &#123;</span><br><span class="line">	...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>关于<code>MappingJackson2HttpMessageConverter</code>的实例化请看下节。</p>
<h3 id="关于HttpMessageConverters的初始化"><a href="#关于HttpMessageConverters的初始化" class="headerlink" title="关于HttpMessageConverters的初始化"></a>关于HttpMessageConverters的初始化</h3><p><code>DispatcherServlet</code>的初始化时会调用<code>initHandlerAdapters(ApplicationContext context)</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initHandlerAdapters</span><span class="params">(ApplicationContext context)</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>.handlerAdapters = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (<span class="built_in">this</span>.detectAllHandlerAdapters) &#123;</span><br><span class="line">			<span class="comment">// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.</span></span><br><span class="line">			Map&lt;String, HandlerAdapter&gt; matchingBeans =</span><br><span class="line">					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">			<span class="keyword">if</span> (!matchingBeans.isEmpty()) &#123;</span><br><span class="line">				<span class="built_in">this</span>.handlerAdapters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(matchingBeans.values());</span><br><span class="line">				<span class="comment">// We keep HandlerAdapters in sorted order.</span></span><br><span class="line">				AnnotationAwareOrderComparator.sort(<span class="built_in">this</span>.handlerAdapters);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">      ...</span><br></pre></td></tr></table></figure>

<p>上述代码会加载<code>ApplicationContext</code>的所有<code>HandlerAdapter</code>，用来处理<code>@RequestMapping</code>的<code>RequestMappingHandlerAdapter</code>实现<code>HandlerAdapter</code>接口，<code>RequestMappingHandlerAdapter</code>也被实例化。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; messageConverters;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">	<span class="keyword">public</span> <span class="title function_">RequestMappingHandlerAdapter</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>.messageConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">ByteArrayHttpMessageConverter</span>());</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">StringHttpMessageConverter</span>());</span><br><span class="line">		<span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">				<span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span>());</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>在构造器中看到<strong>一堆</strong><code>HttpMessageConverter</code>。接着，重点查看<code>AllEncompassingFormHttpMessageConverter</code>类：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span> <span class="keyword">extends</span> <span class="title class_">FormHttpMessageConverter</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Boolean flag controlled by a &#123;<span class="doctag">@code</span> spring.xml.ignore&#125; system property that instructs Spring to</span></span><br><span class="line"><span class="comment">	 * ignore XML, i.e. to not initialize the XML-related infrastructure.</span></span><br><span class="line"><span class="comment">	 * &lt;p&gt;The default is &quot;false&quot;.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="variable">shouldIgnoreXml</span> <span class="operator">=</span> SpringProperties.getFlag(<span class="string">&quot;spring.xml.ignore&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jaxb2Present;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2Present;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2XmlPresent;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2SmilePresent;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> gsonPresent;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jsonbPresent;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> kotlinSerializationJsonPresent;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">static</span> &#123;</span><br><span class="line">		<span class="type">ClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> AllEncompassingFormHttpMessageConverter.class.getClassLoader();</span><br><span class="line">		jaxb2Present = ClassUtils.isPresent(<span class="string">&quot;javax.xml.bind.Binder&quot;</span>, classLoader);</span><br><span class="line">		jackson2Present = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.databind.ObjectMapper&quot;</span>, classLoader) &amp;&amp;</span><br><span class="line">						ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.core.JsonGenerator&quot;</span>, classLoader);</span><br><span class="line">		jackson2XmlPresent = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.dataformat.xml.XmlMapper&quot;</span>, classLoader);</span><br><span class="line">		jackson2SmilePresent = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.dataformat.smile.SmileFactory&quot;</span>, classLoader);</span><br><span class="line">		gsonPresent = ClassUtils.isPresent(<span class="string">&quot;com.google.gson.Gson&quot;</span>, classLoader);</span><br><span class="line">		jsonbPresent = ClassUtils.isPresent(<span class="string">&quot;javax.json.bind.Jsonb&quot;</span>, classLoader);</span><br><span class="line">		kotlinSerializationJsonPresent = ClassUtils.isPresent(<span class="string">&quot;kotlinx.serialization.json.Json&quot;</span>, classLoader);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="title function_">AllEncompassingFormHttpMessageConverter</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				addPartConverter(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">				<span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="keyword">if</span> (jaxb2Present &amp;&amp; !jackson2XmlPresent) &#123;</span><br><span class="line">				addPartConverter(<span class="keyword">new</span> <span class="title class_">Jaxb2RootElementHttpMessageConverter</span>());</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (jackson2Present) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2HttpMessageConverter</span>());<span class="comment">//&lt;----重点看这里</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (gsonPresent) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">GsonHttpMessageConverter</span>());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (jsonbPresent) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">JsonbHttpMessageConverter</span>());</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (kotlinSerializationJsonPresent) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">KotlinSerializationJsonHttpMessageConverter</span>());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (jackson2XmlPresent &amp;&amp; !shouldIgnoreXml) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2XmlHttpMessageConverter</span>());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (jackson2SmilePresent) &#123;</span><br><span class="line">			addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2SmileHttpMessageConverter</span>());</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FormHttpMessageConverter</span> <span class="keyword">implements</span> <span class="title class_">HttpMessageConverter</span>&lt;MultiValueMap&lt;String, ?&gt;&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">private</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; partConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addPartConverter</span><span class="params">(HttpMessageConverter&lt;?&gt; partConverter)</span> &#123;</span><br><span class="line">		Assert.notNull(partConverter, <span class="string">&quot;&#x27;partConverter&#x27; must not be null&quot;</span>);</span><br><span class="line">		<span class="built_in">this</span>.partConverters.add(partConverter);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>在<code>AllEncompassingFormHttpMessageConverter</code>类构造器看到<code>MappingJackson2HttpMessageConverter</code>类的实例化，<code>AllEncompassingFormHttpMessageConverter</code><strong>包含</strong><code>MappingJackson2HttpMessageConverter</code>。</p>
<p> <code>ReturnValueHandler</code>是怎么与<code>MappingJackson2HttpMessageConverter</code>关联起来？请看下节。</p>
<h3 id="ReturnValueHandler与MappingJackson2HttpMessageConverter关联"><a href="#ReturnValueHandler与MappingJackson2HttpMessageConverter关联" class="headerlink" title="ReturnValueHandler与MappingJackson2HttpMessageConverter关联"></a>ReturnValueHandler与MappingJackson2HttpMessageConverter关联</h3><p>再次回顾<code>RequestMappingHandlerAdapter</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> HandlerMethodReturnValueHandlerComposite returnValueHandlers;<span class="comment">//我们关注的returnValueHandlers</span></span><br><span class="line">    </span><br><span class="line">   	</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span><span class="comment">//本方法在AbstractHandlerMethodAdapter</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">			<span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">	&#125;</span><br><span class="line">        </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		ModelAndView mav;</span><br><span class="line">        ...</span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);</span><br><span class="line">        ...</span><br><span class="line">		<span class="keyword">return</span> mav;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">			HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		<span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">			<span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">			<span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">				invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;---我们关注的returnValueHandlers</span></span><br><span class="line">				invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">			&#125;</span><br><span class="line">            </span><br><span class="line">            ...</span><br><span class="line">            </span><br><span class="line">			invocableMethod.invokeAndHandle(webRequest, mavContainer);</span><br><span class="line">			<span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">				<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			webRequest.requestCompleted();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="comment">// Do this first, it may add ResponseBody advice beans</span></span><br><span class="line">		</span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">		<span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers == <span class="literal">null</span>) &#123;<span class="comment">//赋值returnValueHandlers</span></span><br><span class="line">			List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">			<span class="built_in">this</span>.returnValueHandlers = <span class="keyword">new</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span>().addHandlers(handlers);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> List&lt;HandlerMethodReturnValueHandler&gt; <span class="title function_">getDefaultReturnValueHandlers</span><span class="params">()</span> &#123;</span><br><span class="line">		List&lt;HandlerMethodReturnValueHandler&gt; handlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">		...</span><br><span class="line">		<span class="comment">// Annotation-based return value types</span></span><br><span class="line">        <span class="comment">//这里就是 ReturnValueHandler与 MappingJackson2HttpMessageConverter关联 的关键点</span></span><br><span class="line">		handlers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(),<span class="comment">//&lt;---MessageConverters也就传参传进来的</span></span><br><span class="line">				<span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));<span class="comment">//</span></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> handlers;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//------</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; getMessageConverters() &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="built_in">this</span>.messageConverters;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//RequestMappingHandlerAdapter构造器已初始化部分messageConverters</span></span><br><span class="line">   	<span class="keyword">public</span> <span class="title function_">RequestMappingHandlerAdapter</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>.messageConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">ByteArrayHttpMessageConverter</span>());</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">StringHttpMessageConverter</span>());</span><br><span class="line">		<span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">				<span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span>());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">              </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>应用中<code>WebMvcAutoConfiguration</code>（底层是<code>WebMvcConfigurationSupport</code>实现）传入更多<code>messageConverters</code>，其中就包含<code>MappingJackson2HttpMessageConverter</code>。</p>
<h2 id="39、响应处理-【源码分析】-内容协商原理"><a href="#39、响应处理-【源码分析】-内容协商原理" class="headerlink" title="39、响应处理-【源码分析】-内容协商原理"></a>39、响应处理-【源码分析】-内容协商原理</h2><p>根据客户端接收能力不同，返回不同媒体类型的数据。</p>
<p>引入XML依赖：</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.fasterxml.jackson.dataformat<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jackson-dataformat-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>可用Postman软件分别测试返回json和xml：只需要改变请求头中Accept字段（application&#x2F;json、application&#x2F;xml）。</p>
<p>Http协议中规定的，Accept字段告诉服务器本客户端可以接收的数据类型。</p>
<p><strong>内容协商原理</strong>：</p>
<ol>
<li>判断当前响应头中是否已经有确定的媒体类型<code>MediaType</code>。</li>
<li>获取客户端（PostMan、浏览器）支持接收的内容类型。（获取客户端Accept请求头字段application&#x2F;xml）（这一步在下一节有详细介绍）<ul>
<li><code>contentNegotiationManager</code> 内容协商管理器 默认使用基于请求头的策略</li>
<li><code>HeaderContentNegotiationStrategy</code>  确定客户端可以接收的内容类型</li>
</ul>
</li>
<li>遍历循环所有当前系统的 <code>MessageConverter</code>，看谁支持操作这个对象（Person）</li>
<li>找到支持操作Person的converter，把converter支持的媒体类型统计出来</li>
<li>客户端需要application&#x2F;xml，服务端有10种MediaType</li>
<li>进行内容协商的最佳匹配媒体类型</li>
<li>用 <code>支持</code> 将对象转为最佳匹配媒体类型的 converter。调用它进行转化</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//跟上一节的代码一致</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (value <span class="keyword">instanceof</span> CharSequence) &#123;</span><br><span class="line">                body = value.toString();</span><br><span class="line">                valueType = String.class;</span><br><span class="line">                targetType = String.class;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                body = value;</span><br><span class="line">                valueType = getReturnValueType(body, returnType);</span><br><span class="line">                targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">			...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//本节重点</span></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (body != <span class="literal">null</span> &amp;&amp; producibleTypes.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMessageNotWritableException</span>(</span><br><span class="line">                            <span class="string">&quot;No converter found for return value of type: &quot;</span> + valueType);</span><br><span class="line">                &#125;</span><br><span class="line">                List&lt;MediaType&gt; mediaTypesToUse = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">                <span class="keyword">for</span> (MediaType requestedType : acceptableTypes) &#123;</span><br><span class="line">                    <span class="keyword">for</span> (MediaType producibleType : producibleTypes) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (requestedType.isCompatibleWith(producibleType)) &#123;</span><br><span class="line">                            mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (mediaTypesToUse.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                        logger.debug(<span class="string">&quot;No match for &quot;</span> + acceptableTypes + <span class="string">&quot;, supported: &quot;</span> + producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                MediaType.sortBySpecificityAndQuality(mediaTypesToUse);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//选择一个MediaType</span></span><br><span class="line">                <span class="keyword">for</span> (MediaType mediaType : mediaTypesToUse) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mediaType.isConcrete()) &#123;</span><br><span class="line">                        selectedMediaType = mediaType;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) &#123;</span><br><span class="line">                        selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Using &#x27;&quot;</span> + selectedMediaType + <span class="string">&quot;&#x27;, given &quot;</span> +</span><br><span class="line">                            acceptableTypes + <span class="string">&quot; and supported &quot;</span> + producibleTypes);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        	</span><br><span class="line">            <span class="keyword">if</span> (selectedMediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                selectedMediaType = selectedMediaType.removeQualityValue();</span><br><span class="line">                <span class="comment">//本节主角：HttpMessageConverter</span></span><br><span class="line">                <span class="keyword">for</span> (HttpMessageConverter&lt;?&gt; converter : <span class="built_in">this</span>.messageConverters) &#123;</span><br><span class="line">                    <span class="type">GenericHttpMessageConverter</span> <span class="variable">genericConverter</span> <span class="operator">=</span> (converter <span class="keyword">instanceof</span> GenericHttpMessageConverter ?</span><br><span class="line">                            (GenericHttpMessageConverter&lt;?&gt;) converter : <span class="literal">null</span>);</span><br><span class="line">                    </span><br><span class="line">                    <span class="comment">//判断是否可写</span></span><br><span class="line">                    <span class="keyword">if</span> (genericConverter != <span class="literal">null</span> ?</span><br><span class="line">                            ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :</span><br><span class="line">                            converter.canWrite(valueType, selectedMediaType)) &#123;</span><br><span class="line">                        body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,</span><br><span class="line">                                (Class&lt;? <span class="keyword">extends</span> <span class="title class_">HttpMessageConverter</span>&lt;?&gt;&gt;) converter.getClass(),</span><br><span class="line">                                inputMessage, outputMessage);</span><br><span class="line">                        <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                            <span class="type">Object</span> <span class="variable">theBody</span> <span class="operator">=</span> body;</span><br><span class="line">                            LogFormatUtils.traceDebug(logger, traceOn -&gt;</span><br><span class="line">                                    <span class="string">&quot;Writing [&quot;</span> + LogFormatUtils.formatValue(theBody, !traceOn) + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">                            addContentDispositionHeader(inputMessage, outputMessage);</span><br><span class="line">							<span class="comment">//开始写入</span></span><br><span class="line">                            <span class="keyword">if</span> (genericConverter != <span class="literal">null</span>) &#123;</span><br><span class="line">                                genericConverter.write(body, targetType, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">else</span> &#123;</span><br><span class="line">                                ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                                logger.debug(<span class="string">&quot;Nothing to write: null body&quot;</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">			...</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>



<h2 id="40、响应处理-【源码分析】-基于请求参数的内容协商原理"><a href="#40、响应处理-【源码分析】-基于请求参数的内容协商原理" class="headerlink" title="40、响应处理-【源码分析】-基于请求参数的内容协商原理"></a>40、响应处理-【源码分析】-基于请求参数的内容协商原理</h2><p>上一节内容协商原理的第二步：</p>
<p>获取客户端（PostMan、浏览器）支持接收的内容类型。（获取客户端Accept请求头字段application&#x2F;xml）</p>
<ul>
<li><code>contentNegotiationManager</code> 内容协商管理器 默认使用基于请求头的策略</li>
<li><code>HeaderContentNegotiationStrategy</code>  确定客户端可以接收的内容类型</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">		<span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//跟上一节的代码一致</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line">        </span><br><span class="line">        	...</span><br><span class="line">        </span><br><span class="line">                    <span class="comment">//本节重点</span></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line">            ...</span><br><span class="line">            </span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//在AbstractMessageConverterMethodArgumentResolver类内</span></span><br><span class="line">   	<span class="keyword">private</span> List&lt;MediaType&gt; <span class="title function_">getAcceptableMediaTypes</span><span class="params">(HttpServletRequest request)</span></span><br><span class="line">			<span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//内容协商管理器 默认使用基于请求头的策略</span></span><br><span class="line">		<span class="keyword">return</span> <span class="built_in">this</span>.contentNegotiationManager.resolveMediaTypes(<span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request));</span><br><span class="line">	&#125;</span><br><span class="line">        </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ContentNegotiationManager</span> <span class="keyword">implements</span> <span class="title class_">ContentNegotiationStrategy</span>, MediaTypeFileExtensionResolver &#123;</span><br><span class="line">	</span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ContentNegotiationManager</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>(<span class="keyword">new</span> <span class="title class_">HeaderContentNegotiationStrategy</span>());<span class="comment">//内容协商管理器 默认使用基于请求头的策略</span></span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest request)</span> <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line">		<span class="keyword">for</span> (ContentNegotiationStrategy strategy : <span class="built_in">this</span>.strategies) &#123;</span><br><span class="line">			List&lt;MediaType&gt; mediaTypes = strategy.resolveMediaTypes(request);</span><br><span class="line">			<span class="keyword">if</span> (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) &#123;</span><br><span class="line">				<span class="keyword">continue</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> mediaTypes;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">	&#125;</span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//基于请求头的策略</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeaderContentNegotiationStrategy</span> <span class="keyword">implements</span> <span class="title class_">ContentNegotiationStrategy</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * &#123;<span class="doctag">@inheritDoc</span>&#125;</span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@throws</span> HttpMediaTypeNotAcceptableException if the &#x27;Accept&#x27; header cannot be parsed</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest request)</span></span><br><span class="line">			<span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">		String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);</span><br><span class="line">		<span class="keyword">if</span> (headerValueArray == <span class="literal">null</span>) &#123;</span><br><span class="line">			<span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		List&lt;String&gt; headerValues = Arrays.asList(headerValueArray);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			List&lt;MediaType&gt; mediaTypes = MediaType.parseMediaTypes(headerValues);</span><br><span class="line">			MediaType.sortBySpecificityAndQuality(mediaTypes);</span><br><span class="line">			<span class="keyword">return</span> !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (InvalidMediaTypeException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(</span><br><span class="line">					<span class="string">&quot;Could not parse &#x27;Accept&#x27; header &quot;</span> + headerValues + <span class="string">&quot;: &quot;</span> + ex.getMessage());</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="开启浏览器参数方式内容协商功能"><a href="#开启浏览器参数方式内容协商功能" class="headerlink" title="开启浏览器参数方式内容协商功能"></a>开启浏览器参数方式内容协商功能</h3><p>为了方便内容协商，开启基于请求参数的内容协商功能。</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">contentnegotiation:</span></span><br><span class="line">      <span class="attr">favor-parameter:</span> <span class="literal">true</span>  <span class="comment">#开启请求参数内容协商模式</span></span><br></pre></td></tr></table></figure>

<p>内容协商管理器，就会多了一个<code>ParameterContentNegotiationStrategy</code>（由Spring容器注入）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterContentNegotiationStrategy</span> <span class="keyword">extends</span> <span class="title class_">AbstractMappingContentNegotiationStrategy</span> &#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="type">String</span> <span class="variable">parameterName</span> <span class="operator">=</span> <span class="string">&quot;format&quot;</span>;<span class="comment">//</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Create an instance with the given map of file extensions and media types.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="title function_">ParameterContentNegotiationStrategy</span><span class="params">(Map&lt;String, MediaType&gt; mediaTypes)</span> &#123;</span><br><span class="line">		<span class="built_in">super</span>(mediaTypes);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Set the name of the parameter to use to determine requested media types.</span></span><br><span class="line"><span class="comment">	 * &lt;p&gt;By default this is set to &#123;<span class="doctag">@code</span> &quot;format&quot;&#125;.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setParameterName</span><span class="params">(String parameterName)</span> &#123;</span><br><span class="line">		Assert.notNull(parameterName, <span class="string">&quot;&#x27;parameterName&#x27; is required&quot;</span>);</span><br><span class="line">		<span class="built_in">this</span>.parameterName = parameterName;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> String <span class="title function_">getParameterName</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="built_in">this</span>.parameterName;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">protected</span> String <span class="title function_">getMediaTypeKey</span><span class="params">(NativeWebRequest request)</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> request.getParameter(getParameterName());</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//---以下方法在AbstractMappingContentNegotiationStrategy类</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest webRequest)</span></span><br><span class="line">			<span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * An alternative to &#123;<span class="doctag">@link</span> #resolveMediaTypes(NativeWebRequest)&#125; that accepts</span></span><br><span class="line"><span class="comment">	 * an already extracted key.</span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@since</span> 3.2.16</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypeKey</span><span class="params">(NativeWebRequest webRequest, <span class="meta">@Nullable</span> String key)</span></span><br><span class="line">			<span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (StringUtils.hasText(key)) &#123;</span><br><span class="line">			<span class="type">MediaType</span> <span class="variable">mediaType</span> <span class="operator">=</span> lookupMediaType(key);</span><br><span class="line">			<span class="keyword">if</span> (mediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">				handleMatch(key, mediaType);</span><br><span class="line">				<span class="keyword">return</span> Collections.singletonList(mediaType);</span><br><span class="line">			&#125;</span><br><span class="line">			mediaType = handleNoMatch(webRequest, key);</span><br><span class="line">			<span class="keyword">if</span> (mediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">				addMapping(key, mediaType);</span><br><span class="line">				<span class="keyword">return</span> Collections.singletonList(mediaType);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后，浏览器地址输入带format参数的URL：</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">http://localhost:8080/test/person?format=json</span><br><span class="line">或</span><br><span class="line">http://localhost:8080/test/person?format=xml</span><br></pre></td></tr></table></figure>

<p>这样，后端会根据参数format的值，返回对应json或xml格式的数据。</p>
<h2 id="41、响应处理-【源码分析】-自定义MessageConverter"><a href="#41、响应处理-【源码分析】-自定义MessageConverter" class="headerlink" title="41、响应处理-【源码分析】-自定义MessageConverter"></a>41、响应处理-【源码分析】-自定义MessageConverter</h2><p><strong>实现多协议数据兼容。json、xml、x-guigu</strong>（这个是自创的）</p>
<ol>
<li><p><code>@ResponseBody</code> 响应数据出去 调用 <code>RequestResponseBodyMethodProcessor</code> 处理</p>
</li>
<li><p>Processor 处理方法返回值。通过 <code>MessageConverter</code>处理</p>
</li>
<li><p>所有 <code>MessageConverter</code> 合起来可以支持各种媒体类型数据的操作（读、写）</p>
</li>
<li><p>内容协商找到最终的 <code>messageConverter</code></p>
</li>
</ol>
<p>SpringMVC的什么功能，一个入口给容器中添加一个  <code>WebMvcConfigurer</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">extendMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> &#123;</span><br><span class="line">                converters.add(<span class="keyword">new</span> <span class="title class_">GuiguMessageConverter</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义的Converter</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GuiguMessageConverter</span> <span class="keyword">implements</span> <span class="title class_">HttpMessageConverter</span>&lt;Person&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canRead</span><span class="params">(Class&lt;?&gt; clazz, MediaType mediaType)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canWrite</span><span class="params">(Class&lt;?&gt; clazz, MediaType mediaType)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> clazz.isAssignableFrom(Person.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器要统计所有MessageConverter都能写出哪些内容类型</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * application/x-guigu</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">getSupportedMediaTypes</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> MediaType.parseMediaTypes(<span class="string">&quot;application/x-guigu&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">read</span><span class="params">(Class&lt;? extends Person&gt; clazz, HttpInputMessage inputMessage)</span> <span class="keyword">throws</span> IOException, HttpMessageNotReadableException &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(Person person, MediaType contentType, HttpOutputMessage outputMessage)</span> <span class="keyword">throws</span> IOException, HttpMessageNotWritableException &#123;</span><br><span class="line">        <span class="comment">//自定义协议数据的写出</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">data</span> <span class="operator">=</span> person.getUserName()+<span class="string">&quot;;&quot;</span>+person.getAge()+<span class="string">&quot;;&quot;</span>+person.getBirth();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//写出去</span></span><br><span class="line">        <span class="type">OutputStream</span> <span class="variable">body</span> <span class="operator">=</span> outputMessage.getBody();</span><br><span class="line">        body.write(data.getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponseTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 1、浏览器发请求直接返回 xml    [application/xml]        jacksonXmlConverter</span></span><br><span class="line"><span class="comment">     * 2、如果是ajax请求 返回 json   [application/json]      jacksonJsonConverter</span></span><br><span class="line"><span class="comment">     * 3、如果硅谷app发请求，返回自定义协议数据  [appliaction/x-guigu]   xxxxConverter</span></span><br><span class="line"><span class="comment">     *          属性值1;属性值2;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 步骤：</span></span><br><span class="line"><span class="comment">     * 1、添加自定义的MessageConverter进系统底层</span></span><br><span class="line"><span class="comment">     * 2、系统底层就会统计出所有MessageConverter能操作哪些类型</span></span><br><span class="line"><span class="comment">     * 3、客户端内容协商 [guigu---&gt;guigu]</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 作业：如何以参数的方式进行内容协商</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ResponseBody</span>  <span class="comment">//利用返回值处理器里面的消息转换器进行处理</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &quot;/test/person&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">getPerson</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        person.setAge(<span class="number">28</span>);</span><br><span class="line">        person.setBirth(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        person.setUserName(<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>用Postman发送<code>/test/person</code>（请求头<code>Accept:application/x-guigu</code>)，将返回自定义协议数据的写出。</p>
<h2 id="42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配"><a href="#42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配" class="headerlink" title="42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配"></a>42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配</h2><p>假设你想基于自定义请求参数的自定义内容协商功能。</p>
<p>换句话，在地址栏输入<code>http://localhost:8080/test/person?format=gg</code>返回数据，跟<code>http://localhost:8080/test/person</code>且请求头参数<code>Accept:application/x-guigu</code>的返回自定义协议数据的一致。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> <span class="comment">/*implements WebMvcConfigurer*/</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//1、WebMvcConfigurer定制化SpringMVC的功能</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 自定义内容协商策略</span></span><br><span class="line"><span class="comment">             * <span class="doctag">@param</span> configurer</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configureContentNegotiation</span><span class="params">(ContentNegotiationConfigurer configurer)</span> &#123;</span><br><span class="line">                <span class="comment">//Map&lt;String, MediaType&gt; mediaTypes</span></span><br><span class="line">                Map&lt;String, MediaType&gt; mediaTypes = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">                mediaTypes.put(<span class="string">&quot;json&quot;</span>,MediaType.APPLICATION_JSON);</span><br><span class="line">                mediaTypes.put(<span class="string">&quot;xml&quot;</span>,MediaType.APPLICATION_XML);</span><br><span class="line">                <span class="comment">//自定义媒体类型</span></span><br><span class="line">                mediaTypes.put(<span class="string">&quot;gg&quot;</span>,MediaType.parseMediaType(<span class="string">&quot;application/x-guigu&quot;</span>));</span><br><span class="line">                <span class="comment">//指定支持解析哪些参数对应的哪些媒体类型</span></span><br><span class="line">                <span class="type">ParameterContentNegotiationStrategy</span> <span class="variable">parameterStrategy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ParameterContentNegotiationStrategy</span>(mediaTypes);</span><br><span class="line"><span class="comment">//                parameterStrategy.setParameterName(&quot;ff&quot;);</span></span><br><span class="line"></span><br><span class="line">                <span class="comment">//还需添加请求头处理策略，否则accept:application/json、application/xml则会失效</span></span><br><span class="line">                <span class="type">HeaderContentNegotiationStrategy</span> <span class="variable">headeStrategy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HeaderContentNegotiationStrategy</span>();</span><br><span class="line"></span><br><span class="line">                configurer.strategies(Arrays.asList(parameterStrategy, headeStrategy));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>日后开发要注意，<strong>有可能我们添加的自定义的功能会覆盖默认很多功能，导致一些默认的功能失效。</strong></p>
<h2 id="43、视图解析-Thymeleaf初体验"><a href="#43、视图解析-Thymeleaf初体验" class="headerlink" title="43、视图解析-Thymeleaf初体验"></a>43、视图解析-Thymeleaf初体验</h2><blockquote>
<p><strong>Thymeleaf</strong> is a modern server-side Java template engine for both web and standalone environments.</p>
<p>Thymeleaf’s main goal is to bring elegant <em>natural templates</em> to your development workflow — HTML that can be correctly displayed in browsers and also work as static prototypes, allowing for stronger collaboration in development teams.</p>
<p>With modules for Spring Framework, a host of integrations with your favourite tools, and the ability to plug in your own functionality, Thymeleaf is ideal for modern-day HTML5 JVM web development — although there is much more it can do.——<a target="_blank" rel="noopener" href="https://www.thymeleaf.org/">Link</a></p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/documentation.html">Thymeleaf官方文档</a></p>
<h3 id="thymeleaf使用"><a href="#thymeleaf使用" class="headerlink" title="thymeleaf使用"></a>thymeleaf使用</h3><h4 id="引入Starter"><a href="#引入Starter" class="headerlink" title="引入Starter"></a>引入Starter</h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="自动配置好了thymeleaf"><a href="#自动配置好了thymeleaf" class="headerlink" title="自动配置好了thymeleaf"></a>自动配置好了thymeleaf</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(ThymeleafProperties.class)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; TemplateMode.class, SpringTemplateEngine.class &#125;)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThymeleafAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>自动配好的策略</p>
<ol>
<li><p>所有thymeleaf的配置值都在 ThymeleafProperties</p>
</li>
<li><p>配置好了 <strong>SpringTemplateEngine</strong> </p>
</li>
<li><p>配好了 <strong>ThymeleafViewResolver</strong> </p>
</li>
<li><p>我们只需要直接开发页面</p>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_PREFIX</span> <span class="operator">=</span> <span class="string">&quot;classpath:/templates/&quot;</span>;<span class="comment">//模板放置处</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_SUFFIX</span> <span class="operator">=</span> <span class="string">&quot;.html&quot;</span>;<span class="comment">//文件的后缀名</span></span><br></pre></td></tr></table></figure>

<p>编写一个控制层：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ViewTestController</span> &#123;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(Model model)</span>&#123;</span><br><span class="line">        <span class="comment">//model中的数据会被放在请求域中 request.setAttribute(&quot;a&quot;,aa)</span></span><br><span class="line">        model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;一定要大力发展工业文化&quot;</span>);</span><br><span class="line">        model.addAttribute(<span class="string">&quot;link&quot;</span>,<span class="string">&quot;http://www.baidu.com&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;success&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>/templates/success.html</code>：</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">xmlns:th</span>=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;msg&#125;&quot;</span>&gt;</span>nice<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h2</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;www.baidu.com&quot;</span> <span class="attr">th:href</span>=<span class="string">&quot;$&#123;link&#125;&quot;</span>&gt;</span>去百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span>  <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;www.google.com&quot;</span> <span class="attr">th:href</span>=<span class="string">&quot;@&#123;/link&#125;&quot;</span>&gt;</span>去百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<hr>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">servlet:</span></span><br><span class="line">    <span class="attr">context-path:</span> <span class="string">/app</span> <span class="comment">#设置应用名</span></span><br></pre></td></tr></table></figure>

<p>这个设置后，URL要插入<code>/app</code>,  如<code>http://localhost:8080/app/hello.html</code>。</p>
<h3 id="基本语法-1"><a href="#基本语法-1" class="headerlink" title="基本语法"></a>基本语法</h3><h4 id="表达式"><a href="#表达式" class="headerlink" title="表达式"></a>表达式</h4><table>
<thead>
<tr>
<th>表达式名字</th>
<th>语法</th>
<th align="center">用途</th>
</tr>
</thead>
<tbody><tr>
<td>变量取值</td>
<td>${…}</td>
<td align="center">获取请求域、session域、对象等值</td>
</tr>
<tr>
<td>选择变量</td>
<td>*{…}</td>
<td align="center">获取上下文对象值</td>
</tr>
<tr>
<td>消息</td>
<td>#{…}</td>
<td align="center">获取国际化等值</td>
</tr>
<tr>
<td>链接</td>
<td>@{…}</td>
<td align="center">生成链接</td>
</tr>
<tr>
<td>片段表达式</td>
<td>~{…}</td>
<td align="center">jsp:include 作用，引入公共页面片段</td>
</tr>
</tbody></table>
<h4 id="字面量"><a href="#字面量" class="headerlink" title="字面量"></a>字面量</h4><ul>
<li>文本值: <strong>‘one text’</strong> <strong>,</strong> <strong>‘Another one!’</strong> <strong>,…</strong></li>
<li>数字: <strong>0</strong> <strong>,</strong> <strong>34</strong> <strong>,</strong> <strong>3.0</strong> <strong>,</strong> <strong>12.3</strong> <strong>,…</strong></li>
<li>布尔值: <strong>true</strong> <strong>,</strong> <strong>false</strong></li>
<li>空值: <strong>null</strong></li>
<li>变量： one，two，…. 变量不能有空格</li>
</ul>
<h4 id="文本操作"><a href="#文本操作" class="headerlink" title="文本操作"></a>文本操作</h4><ul>
<li>字符串拼接: <strong>+</strong></li>
<li>变量替换: <strong>|The name is ${name}|</strong></li>
</ul>
<h4 id="数学运算"><a href="#数学运算" class="headerlink" title="数学运算"></a>数学运算</h4><ul>
<li>运算符: + , - , * , &#x2F; , %</li>
</ul>
<h4 id="布尔运算"><a href="#布尔运算" class="headerlink" title="布尔运算"></a>布尔运算</h4><ul>
<li>运算符:  <strong>and</strong> <strong>,</strong> <strong>or</strong></li>
<li>一元运算: <strong>!</strong> <strong>,</strong> <strong>not</strong></li>
</ul>
<h4 id="比较运算"><a href="#比较运算" class="headerlink" title="比较运算"></a>比较运算</h4><ul>
<li>比较: <strong>&gt;</strong> <strong>,</strong> <strong>&lt;** **,** **&gt;&#x3D;</strong> <strong>,</strong> <strong>&lt;&#x3D;</strong> <strong>(</strong> <strong>gt</strong> <strong>,</strong> <strong>lt</strong> <strong>,</strong> <strong>ge</strong> <strong>,</strong> <strong>le</strong> <strong>)</strong></li>
<li>等式: <strong>&#x3D;&#x3D;</strong> <strong>,</strong> <strong>!&#x3D;</strong> <strong>(</strong> <strong>eq</strong> <strong>,</strong> <strong>ne</strong> <strong>)</strong></li>
</ul>
<h4 id="条件运算"><a href="#条件运算" class="headerlink" title="条件运算"></a>条件运算</h4><ul>
<li>If-then: <strong>(if) ? (then)</strong></li>
<li>If-then-else: <strong>(if) ? (then) : (else)</strong></li>
<li>Default: (value) <strong>?: (defaultvalue)</strong></li>
</ul>
<h4 id="特殊操作"><a href="#特殊操作" class="headerlink" title="特殊操作"></a>特殊操作</h4><ul>
<li>无操作： _</li>
</ul>
<h3 id="设置属性值-th-attr"><a href="#设置属性值-th-attr" class="headerlink" title="设置属性值-th:attr"></a>设置属性值-th:attr</h3><ul>
<li>设置单个值</li>
</ul>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;subscribe.html&quot;</span> <span class="attr">th:attr</span>=<span class="string">&quot;action=@&#123;/subscribe&#125;&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">fieldset</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;email&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Subscribe!&quot;</span> <span class="attr">th:attr</span>=<span class="string">&quot;value=#&#123;subscribe.submit&#125;&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">fieldset</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>设置多个值</li>
</ul>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;../../images/gtvglogo.png&quot;</span>  </span></span><br><span class="line"><span class="tag">     <span class="attr">th:attr</span>=<span class="string">&quot;src=@&#123;/images/gtvglogo.png&#125;,title=#&#123;logo&#125;,alt=#&#123;logo&#125;&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-attribute-values">官方文档 - 5 Setting Attribute Values</a></p>
<h3 id="迭代"><a href="#迭代" class="headerlink" title="迭代"></a>迭代</h3><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span> <span class="attr">th:each</span>=<span class="string">&quot;prod : $&#123;prods&#125;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.name&#125;&quot;</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.price&#125;&quot;</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.inStock&#125;? #&#123;true&#125; : #&#123;false&#125;&quot;</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span> <span class="attr">th:each</span>=<span class="string">&quot;prod,iterStat : $&#123;prods&#125;&quot;</span> <span class="attr">th:class</span>=<span class="string">&quot;$&#123;iterStat.odd&#125;? &#x27;odd&#x27;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.name&#125;&quot;</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.price&#125;&quot;</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.inStock&#125;? #&#123;true&#125; : #&#123;false&#125;&quot;</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="条件运算-1"><a href="#条件运算-1" class="headerlink" title="条件运算"></a>条件运算</h3><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;comments.html&quot;</span></span></span><br><span class="line"><span class="tag">	<span class="attr">th:href</span>=<span class="string">&quot;@&#123;/product/comments(prodId=$&#123;prod.id&#125;)&#125;&quot;</span></span></span><br><span class="line"><span class="tag">	<span class="attr">th:if</span>=<span class="string">&quot;$&#123;not #lists.isEmpty(prod.comments)&#125;&quot;</span>&gt;</span>view<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">th:switch</span>=<span class="string">&quot;$&#123;user.role&#125;&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;&#x27;admin&#x27;&quot;</span>&gt;</span>User is an administrator<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;#&#123;roles.manager&#125;&quot;</span>&gt;</span>User is a manager<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;*&quot;</span>&gt;</span>User is some other thing<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="属性优先级"><a href="#属性优先级" class="headerlink" title="属性优先级"></a>属性优先级</h3><table>
<thead>
<tr>
<th align="left">Order</th>
<th align="left">Feature</th>
<th align="left">Attributes</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left">Fragment inclusion</td>
<td align="left"><code>th:insert</code> <code>th:replace</code></td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">Fragment iteration</td>
<td align="left"><code>th:each</code></td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">Conditional evaluation</td>
<td align="left"><code>th:if</code> <code>th:unless</code> <code>th:switch</code> <code>th:case</code></td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">Local variable definition</td>
<td align="left"><code>th:object</code> <code>th:with</code></td>
</tr>
<tr>
<td align="left">5</td>
<td align="left">General attribute modification</td>
<td align="left"><code>th:attr</code> <code>th:attrprepend</code> <code>th:attrappend</code></td>
</tr>
<tr>
<td align="left">6</td>
<td align="left">Specific attribute modification</td>
<td align="left"><code>th:value</code> <code>th:href</code> <code>th:src</code> <code>...</code></td>
</tr>
<tr>
<td align="left">7</td>
<td align="left">Text (tag body modification)</td>
<td align="left"><code>th:text</code> <code>th:utext</code></td>
</tr>
<tr>
<td align="left">8</td>
<td align="left">Fragment specification</td>
<td align="left"><code>th:fragment</code></td>
</tr>
<tr>
<td align="left">9</td>
<td align="left">Fragment removal</td>
<td align="left"><code>th:remove</code></td>
</tr>
</tbody></table>
<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#attribute-precedence">官方文档 - 10 Attribute Precedence</a></p>
<h2 id="44、web实验-后台管理系统基本功能"><a href="#44、web实验-后台管理系统基本功能" class="headerlink" title="44、web实验-后台管理系统基本功能"></a>44、web实验-后台管理系统基本功能</h2><h3 id="项目创建"><a href="#项目创建" class="headerlink" title="项目创建"></a>项目创建</h3><p>使用IDEA的Spring Initializr。</p>
<ul>
<li>thymeleaf、</li>
<li>web-starter、</li>
<li>devtools、</li>
<li>lombok</li>
</ul>
<h3 id="登陆页面"><a href="#登陆页面" class="headerlink" title="登陆页面"></a>登陆页面</h3><ul>
<li><p><code>/static</code> 放置 css，js等静态资源</p>
</li>
<li><p><code>/templates/login.html</code> 登录页</p>
</li>
</ul>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">xmlns:th</span>=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span><span class="comment">&lt;!-- 要加这玩意thymeleaf才能用 --&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">class</span>=<span class="string">&quot;form-signin&quot;</span> <span class="attr">action</span>=<span class="string">&quot;index.html&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span> <span class="attr">th:action</span>=<span class="string">&quot;@&#123;/login&#125;&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 消息提醒 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">style</span>=<span class="string">&quot;color: red&quot;</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;msg&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;userName&quot;</span> <span class="attr">class</span>=<span class="string">&quot;form-control&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;User ID&quot;</span> <span class="attr">autofocus</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">class</span>=<span class="string">&quot;form-control&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Password&quot;</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">&quot;btn btn-lg btn-login btn-block&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">i</span> <span class="attr">class</span>=<span class="string">&quot;fa fa-check&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>/templates/main.html</code> 主页</li>
</ul>
<p>thymeleaf内联写法：</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>Hello, [[$&#123;session.user.name&#125;]]!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="登录控制层"><a href="#登录控制层" class="headerlink" title="登录控制层"></a>登录控制层</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IndexController</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 来登录页</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &#123;&quot;/&quot;,&quot;/login&quot;&#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">loginPage</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">main</span><span class="params">(User user, HttpSession session, Model model)</span>&#123; <span class="comment">//RedirectAttributes</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(StringUtils.hasLength(user.getUserName()) &amp;&amp; <span class="string">&quot;123456&quot;</span>.equals(user.getPassword()))&#123;</span><br><span class="line">            <span class="comment">//把登陆成功的用户保存起来</span></span><br><span class="line">            session.setAttribute(<span class="string">&quot;loginUser&quot;</span>,user);</span><br><span class="line">            <span class="comment">//登录成功重定向到main.html;  重定向防止表单重复提交</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;redirect:/main.html&quot;</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;账号密码错误&quot;</span>);</span><br><span class="line">            <span class="comment">//回到登录页面</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 去main页面</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/main.html&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">mainPage</span><span class="params">(HttpSession session, Model model)</span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//最好用拦截器,过滤器</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">loginUser</span> <span class="operator">=</span> session.getAttribute(<span class="string">&quot;loginUser&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(loginUser != <span class="literal">null</span>)&#123;</span><br><span class="line">        	<span class="keyword">return</span> <span class="string">&quot;main&quot;</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//session过期，没有登陆过</span></span><br><span class="line">        	<span class="comment">//回到登录页面</span></span><br><span class="line">	        model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;请重新登录&quot;</span>);</span><br><span class="line">    	    <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm">zcm</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-01/">https://gitee.com/zcmmmm/zcmmmm/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-01/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://gitee.com/zcmmmm/zcmmmm" target="_blank">无人深空</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/SpringBoot2/">SpringBoot2</a></div><div class="post_share"><div class="social-share" data-image="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-02/"><img class="prev-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">SpringBoot2入门-02</div></div></a></div><div class="next-post pull-right"><a href="/2022/09/23/SpringMVC/"><img class="next-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">SpringMVC</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-02/" title="SpringBoot2入门-02"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-09-23</div><div class="title">SpringBoot2入门-02</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#SpringBoot2"><span class="toc-text">SpringBoot2</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#02%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-Spring-%E7%94%9F%E6%80%81%E5%9C%88"><span class="toc-text">02、基础入门-Spring 生态圈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Spring-%E8%83%BD%E5%81%9A%E4%BB%80%E4%B9%88"><span class="toc-text">Spring 能做什么</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-%E7%9A%84%E8%83%BD%E5%8A%9B"><span class="toc-text">Spring 的能力</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-%E7%9A%84%E7%94%9F%E6%80%81"><span class="toc-text">Spring 的生态</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring5-%E9%87%8D%E5%A4%A7%E5%8D%87%E7%BA%A7"><span class="toc-text">Spring5 重大升级</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8-SpringBoot"><span class="toc-text">为什么用 SpringBoot</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#SpringBoot-%E4%BC%98%E7%82%B9"><span class="toc-text">SpringBoot 优点</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#SpringBoot-%E7%BC%BA%E7%82%B9"><span class="toc-text">SpringBoot 缺点</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#03%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-SpringBoot%E7%9A%84%E5%A4%A7%E6%97%B6%E4%BB%A3%E8%83%8C%E6%99%AF"><span class="toc-text">03、基础入门-SpringBoot的大时代背景</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BE%AE%E6%9C%8D%E5%8A%A1"><span class="toc-text">微服务</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F"><span class="toc-text">分布式</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E7%9A%84%E5%9B%B0%E9%9A%BE"><span class="toc-text">分布式的困难</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E7%9A%84%E8%A7%A3%E5%86%B3"><span class="toc-text">分布式的解决</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%91%E5%8E%9F%E7%94%9F"><span class="toc-text">云原生</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8A%E4%BA%91%E7%9A%84%E5%9B%B0%E9%9A%BE"><span class="toc-text">上云的困难</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8A%E4%BA%91%E7%9A%84%E8%A7%A3%E5%86%B3"><span class="toc-text">上云的解决</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#04%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-SpringBoot%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3%E6%9E%B6%E6%9E%84"><span class="toc-text">04、基础入门-SpringBoot官方文档架构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%98%E7%BD%91%E6%96%87%E6%A1%A3%E6%9E%B6%E6%9E%84"><span class="toc-text">官网文档架构</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#05%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-SpringBoot-HelloWorld"><span class="toc-text">05、基础入门 - SpringBoot-HelloWorld</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82"><span class="toc-text">系统要求</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Maven%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6"><span class="toc-text">Maven配置文件</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HelloWorld-%E9%A1%B9%E7%9B%AE"><span class="toc-text">HelloWorld 项目</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA-maven-%E5%B7%A5%E7%A8%8B"><span class="toc-text">创建 maven 工程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96"><span class="toc-text">引入依赖</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E4%B8%BB%E7%A8%8B%E5%BA%8F"><span class="toc-text">创建主程序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BC%96%E5%86%99%E4%B8%9A%E5%8A%A1"><span class="toc-text">编写业务</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C-amp-%E6%B5%8B%E8%AF%95"><span class="toc-text">运行&amp;测试</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%AE%BE%E7%BD%AE%E9%85%8D%E7%BD%AE"><span class="toc-text">设置配置</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%93%E5%8C%85%E9%83%A8%E7%BD%B2"><span class="toc-text">打包部署</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#06%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-SpringBoot-%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86%E7%89%B9%E6%80%A7"><span class="toc-text">06、基础入门-SpringBoot-依赖管理特性</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#07%E3%80%81%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-SpringBoot-%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%89%B9%E6%80%A7"><span class="toc-text">07、基础入门-SpringBoot-自动配置特性</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#08%E3%80%81%E5%BA%95%E5%B1%82%E6%B3%A8%E8%A7%A3-Configuration%E8%AF%A6%E8%A7%A3"><span class="toc-text">08、底层注解-@Configuration详解</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#09%E3%80%81%E5%BA%95%E5%B1%82%E6%B3%A8%E8%A7%A3-Import-%E5%AF%BC%E5%85%A5%E7%BB%84%E4%BB%B6"><span class="toc-text">09、底层注解-@Import 导入组件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#10%E3%80%81%E5%BA%95%E5%B1%82%E6%B3%A8%E8%A7%A3-Conditional-%E6%9D%A1%E4%BB%B6%E8%A3%85%E9%85%8D"><span class="toc-text">10、底层注解-@Conditional 条件装配</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#11%E3%80%81%E5%BA%95%E5%B1%82%E6%B3%A8%E8%A7%A3-ImportResource-%E5%AF%BC%E5%85%A5-Spring-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6"><span class="toc-text">11、底层注解-@ImportResource 导入 Spring 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#12%E3%80%81%E5%BA%95%E5%B1%82%E6%B3%A8%E8%A7%A3-ConfigurationProperties-%E9%85%8D%E7%BD%AE%E7%BB%91%E5%AE%9A"><span class="toc-text">12、底层注解-@ConfigurationProperties 配置绑定</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#13%E3%80%81%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%87%AA%E5%8A%A8%E5%8C%85%E8%A7%84%E5%88%99%E5%8E%9F%E7%90%86"><span class="toc-text">13、自动配置【源码分析】- 自动包规则原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#SpringBootConfiguration"><span class="toc-text">@SpringBootConfiguration</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ComponentScan"><span class="toc-text">@ComponentScan</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#EnableAutoConfiguration"><span class="toc-text">@EnableAutoConfiguration</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#AutoConfigurationPackage"><span class="toc-text">@AutoConfigurationPackage</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#14%E3%80%81%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E5%88%9D%E5%A7%8B%E5%8A%A0%E8%BD%BD%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%B1%BB"><span class="toc-text">14、自动配置【源码分析】-初始加载自动配置类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Import-AutoConfigurationImportSelector-class"><span class="toc-text">@Import(AutoConfigurationImportSelector.class)</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#15%E3%80%81%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E6%B5%81%E7%A8%8B"><span class="toc-text">15、自动配置【源码分析】-自动配置流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#16%E3%80%81%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-SpringBoot%E5%BA%94%E7%94%A8%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99"><span class="toc-text">16、最佳实践-SpringBoot应用如何编写</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#17%E3%80%81%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-Lombok-%E7%AE%80%E5%8C%96%E5%BC%80%E5%8F%91"><span class="toc-text">17、最佳实践-Lombok 简化开发</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#18%E3%80%81%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-dev-tools"><span class="toc-text">18、最佳实践-dev-tools</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#19%E3%80%81%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-Spring-Initailizr%EF%BC%88%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91%E5%90%91%E5%AF%BC%EF%BC%89"><span class="toc-text">19、最佳实践-Spring Initailizr（项目开发向导）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#20%E3%80%81%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-yaml%E7%9A%84%E7%94%A8%E6%B3%95"><span class="toc-text">20、配置文件-yaml的用法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95"><span class="toc-text">基本语法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B"><span class="toc-text">数据类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E4%BE%8B"><span class="toc-text">实例</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#21%E3%80%81%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E7%BB%91%E5%AE%9A%E7%9A%84%E9%85%8D%E7%BD%AE%E6%8F%90%E7%A4%BA"><span class="toc-text">21、配置文件-自定义类绑定的配置提示</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#22%E3%80%81web-%E5%9C%BA%E6%99%AF-web-%E5%BC%80%E5%8F%91%E7%AE%80%E4%BB%8B"><span class="toc-text">22、web 场景-web 开发简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#23%E3%80%81web%E5%9C%BA%E6%99%AF-%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E8%A7%84%E5%88%99%E4%B8%8E%E5%AE%9A%E5%88%B6%E5%8C%96"><span class="toc-text">23、web场景-静态资源规则与定制化</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E7%9B%AE%E5%BD%95"><span class="toc-text">静态资源目录</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E8%AE%BF%E9%97%AE%E5%89%8D%E7%BC%80"><span class="toc-text">静态资源访问前缀</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#webjar"><span class="toc-text">webjar</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#24%E3%80%81web%E5%9C%BA%E6%99%AF-welcome-%E4%B8%8E-favicon-%E5%8A%9F%E8%83%BD"><span class="toc-text">24、web场景-welcome 与 favicon 功能</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AC%A2%E8%BF%8E%E9%A1%B5%E6%94%AF%E6%8C%81"><span class="toc-text">欢迎页支持</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89Favicon"><span class="toc-text">自定义Favicon</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#25%E3%80%81web%E5%9C%BA%E6%99%AF-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E5%8E%9F%E7%90%86"><span class="toc-text">25、web场景-【源码分析】-静态资源原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%85%8D%E7%BD%AE%E7%B1%BB%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E6%9C%89%E5%8F%82%E6%9E%84%E9%80%A0%E5%99%A8"><span class="toc-text">配置类只有一个有参构造器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B5%84%E6%BA%90%E5%A4%84%E7%90%86%E7%9A%84%E9%BB%98%E8%AE%A4%E8%A7%84%E5%88%99"><span class="toc-text">资源处理的默认规则</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AC%A2%E8%BF%8E%E9%A1%B5%E7%9A%84%E5%A4%84%E7%90%86%E8%A7%84%E5%88%99"><span class="toc-text">欢迎页的处理规则</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#26%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-Rest%E6%98%A0%E5%B0%84%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90"><span class="toc-text">26、请求处理-【源码分析】-Rest映射及源码解析</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AF%B7%E6%B1%82%E6%98%A0%E5%B0%84"><span class="toc-text">请求映射</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#27%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E6%80%8E%E4%B9%88%E6%94%B9%E5%8F%98%E9%BB%98%E8%AE%A4%E7%9A%84-method"><span class="toc-text">27、请求处理-【源码分析】-怎么改变默认的_method</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#28%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%AF%B7%E6%B1%82%E6%98%A0%E5%B0%84%E5%8E%9F%E7%90%86"><span class="toc-text">28、请求处理-【源码分析】-请求映射原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#29%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E5%B8%B8%E7%94%A8%E5%8F%82%E6%95%B0%E6%B3%A8%E8%A7%A3%E4%BD%BF%E7%94%A8"><span class="toc-text">29、请求处理-常用参数注解使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#30%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-RequestAttribute"><span class="toc-text">30、请求处理-@RequestAttribute</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#31%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-MatrixVariable-%E4%B8%8E-UrlPathHelper"><span class="toc-text">31、请求处理-@MatrixVariable 与 UrlPathHelper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#32%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E5%90%84%E7%A7%8D%E7%B1%BB%E5%9E%8B%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%8E%9F%E7%90%86"><span class="toc-text">32、请求处理-【源码分析】-各种类型参数解析原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerAdapter"><span class="toc-text">HandlerAdapter</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A7%E8%A1%8C%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95"><span class="toc-text">执行目标方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">参数解析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%80%BC%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-text">返回值处理器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9E%E9%A1%BE%E6%89%A7%E8%A1%8C%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95"><span class="toc-text">回顾执行目标方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9A%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95%E6%AF%8F%E4%B8%80%E4%B8%AA%E5%8F%82%E6%95%B0%E7%9A%84%E5%80%BC"><span class="toc-text">如何确定目标方法每一个参数的值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E7%BB%93"><span class="toc-text">小结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#33%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-Servlet-API-%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%8E%9F%E7%90%86"><span class="toc-text">33、请求处理-【源码分析】-Servlet API 参数解析原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#34%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-Model%E3%80%81Map%E5%8E%9F%E7%90%86"><span class="toc-text">34、请求处理-【源码分析】-Model、Map原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#35%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A%E5%8E%9F%E7%90%86"><span class="toc-text">35、请求处理-【源码分析】-自定义参数绑定原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#36%E3%80%81%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%87%AA%E5%AE%9A%E4%B9%89Converter%E5%8E%9F%E7%90%86"><span class="toc-text">36、请求处理-【源码分析】-自定义Converter原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#37%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-ReturnValueHandler%E5%8E%9F%E7%90%86"><span class="toc-text">37、响应处理-【源码分析】-ReturnValueHandler原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#38%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-HTTPMessageConverter%E5%8E%9F%E7%90%86"><span class="toc-text">38、响应处理-【源码分析】-HTTPMessageConverter原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B3%E4%BA%8EHttpMessageConverters%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-text">关于HttpMessageConverters的初始化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ReturnValueHandler%E4%B8%8EMappingJackson2HttpMessageConverter%E5%85%B3%E8%81%94"><span class="toc-text">ReturnValueHandler与MappingJackson2HttpMessageConverter关联</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#39%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86%E5%8E%9F%E7%90%86"><span class="toc-text">39、响应处理-【源码分析】-内容协商原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#40%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E5%9F%BA%E4%BA%8E%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E7%9A%84%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86%E5%8E%9F%E7%90%86"><span class="toc-text">40、响应处理-【源码分析】-基于请求参数的内容协商原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%80%E5%90%AF%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8F%82%E6%95%B0%E6%96%B9%E5%BC%8F%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86%E5%8A%9F%E8%83%BD"><span class="toc-text">开启浏览器参数方式内容协商功能</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#41%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E8%87%AA%E5%AE%9A%E4%B9%89MessageConverter"><span class="toc-text">41、响应处理-【源码分析】-自定义MessageConverter</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#42%E3%80%81%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86-%E3%80%90%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%91-%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8EPostMan%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86%E5%AE%8C%E5%85%A8%E9%80%82%E9%85%8D"><span class="toc-text">42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#43%E3%80%81%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90-Thymeleaf%E5%88%9D%E4%BD%93%E9%AA%8C"><span class="toc-text">43、视图解析-Thymeleaf初体验</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#thymeleaf%E4%BD%BF%E7%94%A8"><span class="toc-text">thymeleaf使用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%95%E5%85%A5Starter"><span class="toc-text">引入Starter</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%A5%BD%E4%BA%86thymeleaf"><span class="toc-text">自动配置好了thymeleaf</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95-1"><span class="toc-text">基本语法</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A1%A8%E8%BE%BE%E5%BC%8F"><span class="toc-text">表达式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AD%97%E9%9D%A2%E9%87%8F"><span class="toc-text">字面量</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%87%E6%9C%AC%E6%93%8D%E4%BD%9C"><span class="toc-text">文本操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E5%AD%A6%E8%BF%90%E7%AE%97"><span class="toc-text">数学运算</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B8%83%E5%B0%94%E8%BF%90%E7%AE%97"><span class="toc-text">布尔运算</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AF%94%E8%BE%83%E8%BF%90%E7%AE%97"><span class="toc-text">比较运算</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E8%BF%90%E7%AE%97"><span class="toc-text">条件运算</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%89%B9%E6%AE%8A%E6%93%8D%E4%BD%9C"><span class="toc-text">特殊操作</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%BE%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%80%BC-th-attr"><span class="toc-text">设置属性值-th:attr</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%AD%E4%BB%A3"><span class="toc-text">迭代</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E8%BF%90%E7%AE%97-1"><span class="toc-text">条件运算</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B1%9E%E6%80%A7%E4%BC%98%E5%85%88%E7%BA%A7"><span class="toc-text">属性优先级</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#44%E3%80%81web%E5%AE%9E%E9%AA%8C-%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E5%9F%BA%E6%9C%AC%E5%8A%9F%E8%83%BD"><span class="toc-text">44、web实验-后台管理系统基本功能</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%A1%B9%E7%9B%AE%E5%88%9B%E5%BB%BA"><span class="toc-text">项目创建</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%99%BB%E9%99%86%E9%A1%B5%E9%9D%A2"><span class="toc-text">登陆页面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%99%BB%E5%BD%95%E6%8E%A7%E5%88%B6%E5%B1%82"><span class="toc-text">登录控制层</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A8%A1%E5%9E%8B"><span class="toc-text">模型</span></a></li></ol></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By zcm</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script src="/js/categories.js?v1"></script><script src="/js/navigation.js?v1"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>